JavaStreamAPI:终结流与起始流的深入解析
立即解锁
发布时间: 2025-08-19 00:39:08 阅读量: 2 订阅数: 4 


Java Lambda表达式与流API深入解析
### Java Stream API:终结流与起始流的深入解析
#### 1. 终结流:收集与归约
在 Java 编程中,Stream API 提供了强大的功能来处理数据序列。终结流操作主要分为收集(Collection)和归约(Reduction)两类,下面我们来详细探讨它们。
##### 1.1 收集器规则
当收集器按照特定方式执行时,框架和收集器提供的组件之间会发生复杂的交互。为了确保其正确工作,双方都需要遵循一定的规则。
- **框架保证遵循的条件**:
- 新值仅作为累加器的第二个参数出现,其他值均为之前由供应者、累加器或组合器返回的结果。
- 供应者、累加器和组合器的结果可能会返回给 collect 方法的调用者,否则,它们仅作为累加器、组合器或完成器的参数使用。
- 传递给组合器或完成器但未返回的值不会再被使用,其内容已被处理,不应重复使用。
- **收集器必须遵循的约束**:
- 除非具有 CONCURRENT 特性,否则必须确保供应者、累加器或组合器函数返回的任何结果都是线程受限的,即不能被其他线程访问。这使得收集器框架能够并行处理,而无需担心外部线程的干扰。
- 如果具有 CONCURRENT 特性,累加器必须使用相同的可并发修改的结果容器来保证线程安全,因为框架可能会从多个线程并发调用它。当顺序很重要时,不能使用并发收集器。
- **身份约束**:空结果容器与其他元素组合时,应不改变其他元素。更正式地说,对于任何值 s:
```java
s == combiner.apply(s, supplier.get());
s == combiner.apply(supplier.get(), s);
```
- **结合性约束**:在不同位置拆分计算应产生相同的结果。更正式地说,对于任何值 q、r 和 s:
```java
combiner.apply(combiner.apply(q, r), s)) == combiner.apply(q, combiner.apply(r, s));
```
- **兼容性约束**:在累加器和组合器之间以不同方式划分计算应产生相同的结果。更正式地说,对于任何值 r、s 和 t,执行左边两行代码和右边两行代码应得到相同的 r 值:
```java
accumulator.accept(s, t);
r = combiner.apply(r, s);
r = combiner.apply(r, s);
accumulator.accept(r, t);
```
##### 1.2 归约操作
虽然在 Java 程序中,收集通常比归约更有用,但归约在某些情况下仍然非常实用。下面我们分别探讨对基本类型流和引用类型流的归约操作。
- **基本类型流的归约**:
基本类型流的归约遵循分治策略。以 `IntStream` 为例,其归约操作与收集器的图示有两个显著区别:
- 使用一个基值(归约的标识)代替供应者函数创建的空容器实例。
- 由于只涉及一种类型,累加器和组合器是相同的。
`IntStream.reduce` 有两个重载方法:
```java
OptionalInt reduce(IntBinaryOperator);
int reduce(int, IntBinaryOperator);
```
例如,计算整数流的和可以这样实现:
```java
int sum = IntStream.of(1, 2, 3).reduce(0, (a, b) -> a + b);
```
同样,阶乘的计算可以使用归约实现:
```java
int intArgFactorial = IntStream.rangeClosed(1, intArg).reduce(1, (a, b) -> a * b);
```
当流为空时,接受标识的重载方法返回提供的标识,而不接受标识的重载方法返回一个空的 `OptionalInt`。
- **引用类型流的归约**:
`Stream.reduce` 有三个重载方法:
```java
Optional<T> reduce(BinaryOperator<T>);
T reduce(T, BinaryOperator<T>);
U reduce(U, BiFunction<U, T, U>, BinaryOperator<U>);
```
- 第一个重载方法只接受一个组合器,不接受标识,返回一个 `Optional`。例如,查找图书馆中按标题字母顺序排列的第一本书:
```java
Comparator<Book> titleComparator = Comparator.comparing(Book::getTitle);
Optional<Book> first = library.stream().reduce(BinaryOperator.minBy(titleComparator));
```
- 第二个重载方法接受一个标识和一个二元运算符。例如,计算 `BigInteger` 流的和:
```java
Stream<BigInteger> biStream = LongStream.of(1, 2, 3).mapToObj(BigInteger::valueOf);
BigInteger bigIntegerSum = biStream.reduce(BigInteger.ZERO, BigInteger::add);
```
- 第三个重载方法引入了一个累加器,并允许返回不同的类型。例如,计算图书馆中书籍的总卷数:
```java
int totalVolumes = library.stream().reduce(0, (sum, book) -> sum + book.getPageCounts().length, Integer::sum);
```
当然,也可以将其写为单独的 `map` 和 `reduce` 操作:
```java
int totalVolumes = library.stream().mapToInt(b -> b.getPageCounts().length).sum();
```
不过,第三个重载方法在某些情况下可以通过将映射和归约组合成一个函数来实现显著的优化。
##### 1.3 收集器与归约的组合
在 Stream API 中,有一些“下游”收集器可以与其他收集器组合使用。其中,`Collectors.reducing` 方法有三个重载,对应于 `Stream.reduce` 的三个重载:
```ja
```
0
0
复制全文
相关推荐










