java基础-函数式编程 (3)

对于无限序列,如果直接调用forEach()或者count()这些最终求值操作,会进入死循环,因为永远无法计算完这个序列,所以正确的方法是先把无限序列变成有限序列,例如,用limit()方法可以截取前面若干个元素,这样就变成了一个有限序列,对这个有限序列调用forEach()或者count()操作就没有问题。

其他方法

通过一些API提供的接口,直接获得Stream。

例如,Files类的lines()方法可以把一个文件变成一个Stream,每个元素代表文件的一行内容:

try (Stream<String> lines = Files.lines(Paths.get("/path/to/file.txt"))) { ... }

此方法对于按行遍历文本文件十分有用。

另外,正则表达式的Pattern对象有一个splitAsStream()方法,可以直接把一个长字符串分割成Stream序列而不是数组:

Pattern p = Pattern.compile("\\s+"); Stream<String> s = p.splitAsStream("The quick brown fox jumps over the lazy dog"); s.forEach(System.out::println); 基本类型的Stream

因为Java的范型不支持基本类型,所以我们无法用Stream这样的类型,会发生编译错误。
为了保存int,只能使用String,但这样会产生频繁的装箱、拆箱操作。
为了提高效率,Java标准库提供了IntStream、LongStream和DoubleStream这三种使用基本类型的Stream,它们的使用方法和范型Stream没有大的区别,设计这三个Stream的目的是提高运行效率:

// 将int[]数组变为IntStream: IntStream is = Arrays.stream(new int[] { 1, 2, 3 }); // 将Stream<String>转换为LongStream: LongStream ls = List.of("1", "2", "3").stream().mapToLong(Long::parseLong); 3.2 使用map

map()方法用于将一个Stream的每个元素映射成另一个元素并转换成一个新的Stream;

可以将一种元素类型转换成另一种元素类型。

map操作,把一个Stream的每个元素一一对应到应用了目标函数的结果上。

Stream<Integer> s = Stream.of(1, 2, 3, 4, 5); Stream<Integer> s2 = s.map(n -> n * n); Function接口对象

如果我们查看Stream的源码,会发现map()方法接收的对象是Function接口对象,它定义了一个apply()方法,负责把一个T类型转换成R类型:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

其中,Function的定义是:

@FunctionalInterface public interface Function<T, R> { // 将T类型转换为R: R apply(T t); }

利用map(),不但能完成数学计算,对于字符串操作,以及任何Java对象都是非常有用的。例如:

import java.util.*; mport java.util.stream.*; public class Main { public static void main(String[] args) { List.of(" Apple ", " pear ", " ORANGE", " BaNaNa ") .stream() .map(String::trim) // 去空格 .map(String::toLowerCase) // 变小写 .forEach(System.out::println); // 打印 } }

例子:

使用map()把一组String转换为LocalDate并打印。

package com.itranswarp.learnjava; import java.time.LocalDate; import java.util.Arrays; public class Main { public static void main(String[] args) { String[] array = new String[] { " 2019-12-31 ", "2020 - 01-09 ", "2020- 05 - 01 ", "2022 - 02 - 01", " 2025-01 -01" }; Arrays.stream(array) // .map(s -> s.replaceAll("\\s+", "")) //使用正则表达式,将空格删除掉 .map(s -> s.replace(" ", "")) .map(LocalDate::parse) .forEach(System.out::println); } } 3.3 使用filter

Stream.filter()是Stream的另一个常用转换方法。

所谓filter()操作,就是对一个Stream的所有元素一一进行测试,不满足条件的就被“滤掉”了,剩下的满足条件的元素就构成了一个新的Stream。

例如,我们对1,2,3,4,5这个Stream调用filter(),传入的测试函数f(x) = x % 2 != 0用来判断元素是否是奇数,这样就过滤掉偶数,只剩下奇数,因此我们得到了另一个序列1,3,5:

例子:只保留奇数

import java.util.stream.IntStream; public class Main { public static void main(String[] args) { IntStream.of(1, 2, 3, 4, 5, 6, 7, 8, 9) .filter(n -> n % 2 != 0) .forEach(System.out::println); } } Predicate接口对象

filter()方法接收的对象是Predicate接口对象,它定义了一个test()方法,负责判断元素是否符合条件:

@FunctionalInterface public interface Predicate<T> { // 判断元素t是否符合条件: boolean test(T t); }

filter()除了常用于数值外,也可应用于任何Java对象。

例子:从一组给定的LocalData中过滤掉工作日,得到休息日

import java.time.*; import java.util.function.*; import java.util.stream.*; public class Main { public static void main(String[] args) { Stream.generate(new LocalDateSupplier()) .limit(31) .filter(ldt -> ldt.getDayOfWeek() == DayOfWeek.SATURDAY || ldt.getDayOfWeek() == DayOfWeek.SUNDAY) .forEach(System.out::println); } } class LocalDateSupplier implements Supplier<LocalDate> { LocalDate start = LocalDate.of(2020, 1, 1); int n = -1; public LocalDate get() { n++; return start.plusDays(n); } } 3.4 使用reduce

map()和filter()都是Stream的转换方法,而Stream.reduce()则是Stream的一个聚合方法,它可以把一个Stream的所有元素按照聚合函数聚合成一个结果。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypzyy.html