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. 总结

我们通过 reduceskip 两种 API 探讨了获取 Stream 末尾元素的方法,并分析了为何不适用于无限流。

✅ 关键结论:

  • 从 Stream 获取末尾元素比其他数据结构(如 List)更复杂
  • 根本原因在于 Stream 的惰性求值特性:只有终端操作触发时才会执行计算
  • 无法预知当前元素是否为最后一个,这是天然限制

代码示例已托管在 GitHub,欢迎参考。


原始标题:How to Get the Last Element of a Stream in Java?