1. 概述
Java 8 引入的 Stream API 是一项重大特性。Stream 表示惰性求值的对象序列,提供了丰富、流畅且类似单子(monadic)的 API。
本文将快速探讨获取 Stream 中最后一个元素的几种方法。务必注意:由于 Stream 的特性,这并非天然操作。一定要确保你处理的不是无限流,否则会踩坑。
2. 使用 Reduce API
Reduce(归约)的核心作用是将 Stream 中的元素集合缩减为单个元素。
通过归约操作,我们可以将元素集合缩减至仅剩最后一个元素。⚠️ 这种方法只对顺序流(sequential stream)能保证确定性结果。
下面用 List<String>
演示,从 List 获取 Stream 后进行归约:
List<String> valueList = new ArrayList<>();
valueList.add("Joe");
valueList.add("John");
valueList.add("Sean");
Stream<String> stream = valueList.stream();
stream.reduce((first, second) -> second)
.orElse(null);
这里,流被逐步归约,最终只保留最后一个元素。如果流为空,则返回 null
。
3. 使用 Skip 函数
另一种获取末尾元素的方式是跳过前面的所有元素。通过 Stream 类的 skip
函数实现。⚠️ 注意:这种方法会两次消费 Stream,存在明显的性能损耗。
创建字符串 List 后,利用其 size
方法计算需要跳过的元素数量:
List<String> valueList = new ArrayList<String>();
valueList.add("Joe");
valueList.add("John");
valueList.add("Sean");
long count = valueList.stream().count();
Stream<String> stream = valueList.stream();
stream.skip(count - 1).findFirst().get();
最终得到最后一个元素 "Sean"
。
4. 获取无限流的最后一个元素
尝试获取无限流的最后一个元素会导致无限求值循环。无论是 skip
还是 reduce
,除非先用 limit
操作限制元素数量,否则方法调用将永不返回。
以下代码尝试获取无限流的末尾元素:
Stream<Integer> stream = Stream.iterate(0, i -> i + 1);
stream.reduce((first, second) -> second).orElse(null);
结果:程序将卡死在求值过程中,最终导致执行中断。
5. 总结
我们通过 reduce
和 skip
两种 API 探讨了获取 Stream 末尾元素的方法,并分析了为何不适用于无限流。
✅ 关键结论:
- 从 Stream 获取末尾元素比其他数据结构(如 List)更复杂
- 根本原因在于 Stream 的惰性求值特性:只有终端操作触发时才会执行计算
- 无法预知当前元素是否为最后一个,这是天然限制
代码示例已托管在 GitHub,欢迎参考。