Java 8 Stream API - 任何有状态的中间操作都能保证新的源集合吗? [英] Java 8 Stream API - Does any stateful intermediate operation guarantee a new source collection?
问题描述
以下陈述是否属实?
sorted()
操作是有状态中间操作,这意味着后续操作不再对后备集合进行操作,而是对内部状态进行操作。
The
sorted()
operation is a "stateful intermediate operation", which means that subsequent operations no longer operate on the backing collection, but on an internal state.
( Source 和来源 - 它们似乎相互复制或来自同一来源。)
(Source and source - they seem to copy from each other or come from the same source.)
我测试过 Stream :: sorted
作为上述来源的摘录:
I have tested Stream::sorted
as a snippet from sources above:
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 :: sorted
, Stream :: distinct
, Stream :: limit
和 Stream :: skip
:
It works. I replaced Stream::sorted
with Stream::distinct
, Stream::limit
and 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
被抛出。
To my surprise, the NullPointerException
is thrown.
所有测试方法都遵循有状态中间操作特征。然而,的这种独特行为 Stream :: sorted
未记录,流操作和流水线部分解释了有状态中间操作是否真正保证了新的源集合。
All the tested methods follow the stateful intermediate operation characteristics. Yet, this unique behavior of Stream::sorted
is not documented nor the Stream operations and pipelines part explains whether the stateful intermediate operations really guarantee a new source collection.
我的困惑来自哪里以及上述行为的解释是什么?
Where my confusion comes from and what is the explanation of the behavior above?
推荐答案
API文档没有保证后续操作不再对后备集合进行操作,因此,您不应该依赖于特定实现的这种行为。
The API documentation makes no such guarantee "that subsequent operations no longer operate on the backing collection", hence, you should never rely on such a behavior of a particular implementation.
你的例子偶然发生了想要的事情;甚至不能保证由 collect(Collectors.toList())
创建的 List
支持删除
操作。
Your example happens to do the desired thing by accident; there’s not even a guarantee that the List
created by collect(Collectors.toList())
supports the remove
operation.
显示反例
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
显式执行没有指定顺序的操作,因此,排序是不必要的。
throws a ConcurrentModificationException
. The reason is that the implementation optimizes this scenario, as the source is already sorted. In principle, it could do the same optimization to your original example, as forEach
is explicitly performing the action in no specified order, hence, the sorting is unnecessary.
还有其他可以想象的优化,例如 sorted()。findFirst()
可以转换为查找最小操作,而无需将元素复制到新存储中进行排序。
There are other optimizations imaginable, e.g. sorted().findFirst()
could get converted to a "find the minimum" operation, without the need to copy the element into a new storage for sorting.
所以最重要的是,当依赖于未指明的行为时,今天可能发生的事情,明天可能会在新的优化时加入。
So the bottom line is, when relying on unspecified behavior, what may happen to work today, may break tomorrow, when new optimizations are added.
这篇关于Java 8 Stream API - 任何有状态的中间操作都能保证新的源集合吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!