Java流式编程笔记
流是一系列与特定存储机制无关的元素。
流式编程采用 内部迭代
,这是流式编程的核心特性之一。
流操作的类型有三种:创建流,修改流元素(中间操作, Intermediate Operations),消费流元素(终端操作, Terminal Operations)。最后一种类型通常意味着收集流元素(通常是到集合中)。
一、流创建
可以通过
Stream.of()
很容易地将一组元素转化成为流。
- int 类型的范围
IntStream
类提供了range()
方法用于生成整型序列的流。- iterate()
Stream.iterate()
以种子(第一个参数)开头,并将其传给方法(第二个参数)。方法的结果将添加到流,并存储作为第一个参数用于下次调用iterate()
,依次类推。- 流的建造者模式
在建造者设计模式(也称构造器模式)中,首先创建一个builder
对象,传递给它多个构造器信息,最后执行“构造”。Stream 库提供了这样的Builder
。- Arrays
Arrays
类中含有一个名为stream()
的静态方法用于把数组转换成为流。
stream()
同样可以产生 IntStream,LongStream 和 DoubleStream。- 正则表达式
Java 8 在java.util.regex.Pattern
中增加了一个新的方法splitAsStream()
。这个方法可以根据传入的公式将字符序列转化为流。但是有一个限制,输入只能是 CharSequence,因此不能将流作为splitAsStream()
的参数。
二、中间操作
中间操作用于从一个流中获取对象,并将对象作为另一个流从后端输出,以连接到其他操作。
- 跟踪和调试
peek()
操作的目的是帮助调试。它允许你无修改地查看流中的元素。- 流元素排序
在Randoms.java
中,还有另一种形式的实现:传入一个 Comparator 参数。
sorted()
预设了一些默认的比较器。当然也可以把 Lambda 函数作为参数传递给sorted()
。- 移除元素
distinct()
:在Randoms.java
类中的distinct()
可用于消除流中的重复元素。相比创建一个 Set 集合,该方法的工作量要少得多。filter(Predicate)
:过滤操作会保留与传递进去的过滤器函数计算结果为true
元素。
- 应用函数到元素
map(Function)
:将函数操作应用在输入流的元素中,并将返回值传递到输出流中。mapToInt(ToIntFunction)
:操作同上,但结果是 IntStream。mapToLong(ToLongFunction)
:操作同上,但结果是 LongStream。mapToDouble(ToDoubleFunction)
:操作同上,但结果是 DoubleStream。
- 在
map()
中组合流
flatMap() 做了两件事:将产生流的函数应用在每个元素上(与 map() 所做的相同),然后将每个流都扁平化为元素,因而最终产生的仅仅是元素。
flatMap(Function)
:当Function
产生流时使用。flatMapToInt(Function)
:当Function
产生IntStream
时使用。flatMapToLong(Function)
:当Function
产生LongStream
时使用。flatMapToDouble(Function)
:当Function
产生DoubleStream
时使用。flatMap()
可以将元素流的流扁平化为一个简单的元素流。或者,我们可以使用String.split()
生成一个数组,其可以被Arrays.stream()
转化成为流:
三、Optional类
一些标准流操作返回 Optional 对象,因为它们并不能保证预期结果一定存在。包括:
findFirst()
返回一个包含第一个元素的 Optional 对象,如果流为空则返回 Optional.emptyfindAny()
返回包含任意元素的 Optional 对象,如果流为空则返回 Optional.emptymax()
和min()
返回一个包含最大值或者最小值的 Optional 对象,如果流为空则返回 Optional.empty
reduce()
不再以identity
形式开头,而是将其返回值包装在 Optional 中。(identity
对象成为其他形式的reduce()
的默认结果,因此不存在空结果的风险)对于数字流 IntStream、LongStream 和 DoubleStream,
average()
会将结果包装在 Optional 以防止流为空。
- 便利函数
有许多便利函数可以解包 Optional ,这简化了上述“对所包含的对象的检查和执行操作”的过程:
ifPresent(Consumer)
:当值存在时调用 Consumer,否则什么也不做。orElse(otherObject)
:如果值存在则直接返回,否则生成 otherObject。orElseGet(Supplier)
:如果值存在则直接返回,否则使用 Supplier 函数生成一个可替代对象。orElseThrow(Supplier)
:如果值存在直接返回,否则使用 Supplier 函数生成一个异常。
- 创建 Optional
当在代码中加入 Optional 时,可以使用下面 3 个静态方法:
empty()
:生成一个空 Optional。of(value)
:将一个非空值包装到 Optional 里。ofNullable(value)
:针对一个可能为空的值,为空时自动生成 Optional.empty,否则将值包装在 Optional 中。
- Optional 对象操作
当流管道生成了 Optional 对象,下面 3 个方法可使得 Optional 的后续能做更多的操作:
filter(Predicate)
:将 Predicate 应用于 Optional 中的内容并返回结果。当 Optional 不满足 Predicate 时返回空。如果 Optional 为空,则直接返回。map(Function)
:如果 Optional 不为空,应用 Function 于 Optional 中的内容,并返回结果。否则直接返回 Optional.empty。flatMap(Function)
:同map()
,但是提供的映射函数将结果包装在 Optional 对象中,因此flatMap()
不会在最后进行任何包装。以上方法都不适用于数值型 Optional。一般来说,流的
filter()
会在 Predicate 返回false
时移除流元素。而Optional.filter()
在失败时不会删除 Optional,而是将其保留下来,并转化为空。
四、终端操作
以下操作将会获取流的最终结果。至此无法再继续往后传递流。可以说,终端操作(Terminal Operations)总是在流管道中所做的最后一件事。
- 数组
toArray()
:将流转换成适当类型的数组。toArray(generator)
:在特殊情况下,生成自定义类型的数组。
- 循环
forEach(Consumer)
常见如System.out::println
作为 Consumer 函数。forEachOrdered(Consumer)
: 保证forEach
按照原始流顺序操作。
- 集合
collect(Collector)
:使用 Collector 收集流元素到结果集合中。collect(Supplier, BiConsumer, BiConsumer)
:同上,第一个参数 Supplier 创建了一个新结果集合,第二个参数 BiConsumer 将下一个元素包含到结果中,第三个参数 BiConsumer 用于将两个值组合起来。
- 组合
reduce(BinaryOperator)
:使用 BinaryOperator 来组合所有流中的元素。因为流可能为空,其返回值为 Optional。reduce(identity, BinaryOperator)
:功能同上,但是使用 identity 作为其组合的初始值。因此如果流为空,identity 就是结果。reduce(identity, BiFunction, BinaryOperator)
:更复杂的使用形式,这里把它包含在内,因为它可以提高效率。通常,我们可以显式地组合map()
和reduce()
来更简单的表达它。
- 匹配
allMatch(Predicate)
:如果流的每个元素根据提供的 Predicate 都返回 true 时,结果返回为 true。在第一个 false 时,则停止执行计算。anyMatch(Predicate)
:如果流中的任意一个元素根据提供的 Predicate 返回 true 时,结果返回为 true。在第一个 false 是停止执行计算。noneMatch(Predicate)
:如果流的每个元素根据提供的 Predicate 都返回 false 时,结果返回为 true。在第一个 true 时停止执行计算。
- 查找
findFirst()
:返回第一个流元素的 Optional,如果流为空返回 Optional.empty。findAny()
:返回含有任意流元素的 Optional,如果流为空返回 Optional.empty。
- 信息
count()
:流中的元素个数。max(Comparator)
:根据所传入的 Comparator 所决定的“最大”元素。min(Comparator)
:根据所传入的 Comparator 所决定的“最小”元素。
- 数字流信息
average()
:求取流元素平均值。max()
和min()
:数值流操作无需 Comparator。sum()
:对所有流元素进行求和。summaryStatistics()
:生成可能有用的数据。目前并不太清楚这个方法存在的必要性,因为我们其实可以用更直接的方法获得需要的数据。
以上笔记中的内容整理自 On Java 8 中文版
GitHub Pages 完整阅读:《On Java 8》中文版
Gitee Pages 完整阅读:《On Java 8》中文版