[英]Java 8 Stream API - Does any stateful intermediate operation guarantee a new source collection?
以下陈述是真的吗?
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::distinct
, Stream::limit
和Stream::skip
替换了Stream::sorted
:
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操作和管道部分也解释了有状态中间操作是否真正保证了新的源集合。
我的困惑来自何处以及上述行为的解释是什么?
API文档没有保证“后续操作不再对后备集合进行操作”,因此,您永远不应该依赖于特定实现的这种行为。
你的例子偶然发生了想要的事情; 甚至不能保证collect(Collectors.toList())
创建的List
支持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()
可以转换为“查找最小”操作,而无需将元素复制到新存储中进行排序。
因此,最重要的是,当依赖于未指明的行为时,今天可能发生的事情可能会在明天,即添加新的优化时中断。
sorted
好后必须是流管道的完整复制屏障,因为所有源都无法排序 ; 但这并没有记录,因此不依赖它。
这不仅仅是关于sorted
本身,而是可以对流管道进行其他优化,以便可以完全跳过sorted
。 例如:
List<Integer> sortedList = IntStream.range(0, 10)
.boxed()
.collect(Collectors.toList());
StreamSupport.stream(() -> sortedList.spliterator(), Spliterator.SORTED, false)
.sorted()
.forEach(sortedList::remove); // fails with CME, thus no copying occurred
当然, sorted
需要是一个完整的障碍并停止进行整个排序,当然,除非它可以被跳过,因此文档没有做出这样的承诺,因此我们不会遇到奇怪的意外。
distinct
另一方面不必是完整的屏障 ,所有不同的作用是在一个时间检查一个元件,如果是唯一的; 因此,在检查单个元素(并且它是唯一的)之后,它将被传递到下一个阶段,因此不会成为完整的障碍。 无论哪种方式,这也没有记录......
你不应该通过forEach(list::remove)
的终端操作提起案例,因为list::remove
是一个干扰函数,它违反了终端动作的“非干扰”原则。
在了解为什么不正确的代码段导致意外(或未记录)行为之前,遵循规则至关重要。
我相信list::remove
是问题的根源。 如果你为forEach
写了一个合适的动作,你就不会注意到这个场景的操作之间的区别。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.