Java 8 Stream API - 任何有状态的中间操作都能保证新的源集合吗? [英] Java 8 Stream API - Does any stateful intermediate operation guarantee a new source collection?

查看:94
本文介绍了Java 8 Stream API - 任何有状态的中间操作都能保证新的源集合吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下陈述是否属实?


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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆