一尘不染

Java 8 Stream API-是否有状态中间操作保证新的源集合?

java

以下说法正确吗?

sorted()操作是“有状态的中间操作”,这意味着后续操作不再对后备集合进行操作,而是对内部状态进行操作。

来源来源
-它们似乎是彼此复制或来自同一来源。)

我已经Stream::sorted从上面的源代码中测试了一个片段:

final List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList());

list.stream()
    .filter(i -> i > 5)
    .sorted()
    .forEach(list::remove);

System.out.println(list);            // Prints [0, 1, 2, 3, 4, 5]

有用。我换成Stream::sortedStream::distinctStream::limit并且Stream::skip

final List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList());

list.stream()
    .filter(i -> i > 5)
    .distinct()
    .forEach(list::remove);          // Throws NullPointerException

令我惊讶的NullPointerException是,它被抛出了。

所有测试的方法都遵循有状态的中间操作特性。但是,这种独特的行为Stream::sorted没有得到记录,Stream操作和管道部分也没有说明 有状态中间操作 是否真正保证了新的源集合。

我的困惑来自哪里,以上行为的解释是什么?


阅读 182

收藏
2020-12-03

共1个答案

一尘不染

API文档没有这样的保证“后续操作将不再对后备集合进行操作”,因此,您永远不应依赖于特定实现的这种行为。

您的示例碰巧偶然地完成了所需的操作;甚至不能保证List创建的by
collect(Collectors.toList())支持该remove操作。

展示一个反例

Set<Integer> set = IntStream.range(0, 10).boxed()
    .collect(Collectors.toCollection(TreeSet::new));
set.stream()
    .filter(i -> i > 5)
    .sorted()
    .forEach(set::remove);

抛出一个ConcurrentModificationException。原因是实现已优化了此方案,因为源已被排序。原则上,它可以对原始示例进行相同的优化,就像forEach不按指定顺序显式执行操作一样,因此不需要排序。

还有其他可以想象的优化,例如sorted().findFirst()可以转换为“查找最小值”操作,而无需将元素复制到新存储中进行排序。

因此,最重要的是,当依赖于未指定的行为时,今天发生的事情可能会在明天添加新的优化措施而中断。

2020-12-03