列表的奇怪行为

时间:2015-08-12 11:13:40

标签: java

我已经从0-9(Integer)定义了以下列表:

List<Integer> list = 
                IntStream.range(0, 10)
                         .boxed()
                         .collect(Collectors.toCollection(ArrayList::new));

当我尝试使用以下代码删除元素时:

list.stream()
            .peek(list::remove)
            .forEach(System.out::println);

它应该抛出ConcurrentModificationException但有趣的是它适用于某些元素并提供以下输出(在结尾处抛出异常并删除了一些元素):

0
2
4
6
8
null
null
null
null
null
Exception in thread "main" java.util.ConcurrentModificationException

但如果我添加了sorted(),如下所示:

list.stream()
            .sorted()
            .peek(list::remove)
            .forEach(System.out::println);

这完全正常并删除了所有元素,我很困惑为什么stream会这样做。

1 个答案:

答案 0 :(得分:11)

您是否查看了源代码如何实现这些类?

方法sorted()将对您的列表进行复制以进行排序。

因此行为不同:您正在迭代已排序的副本,然后仅从第一个列表中删除。

不鼓励

peek()

不鼓励使用方法peek;来自JavaDoc:

  

此方法主要用于支持调试

可以将System::println放在那里或类似用于开发目的,但它不是您应该用于最终程序的功能。

永远不要尝试修改当前流

阅读流包的API规范:

https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#NonInterference

  

对于大多数数据源,防止干扰意味着确保在流管道执行期间根本不修改数据源。

因此,换句话说:您不能从当前正在播放的列表中删除,但无法保证它可以正常工作。

发生了什么

您的初始列表是

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]        pos = 0

您打印0并将其删除,然后前进到下一个位置。然后新阵列

[1, 2, 3, 4, 5, 6, 7, 8, 9, null]     pos = 1

下一个位置,该值现在为2,因此请打印并移除2

[1, 3, 4, 5, 6, 7, 8, 9, null, null]  pos = 2

等等。问题是,由于你的并发删除,列表的其余部分继续向前发展。

{<1}}会在流端抛出。这似乎只是ConcurrentModificationException的性能决策(仅针对并发修改检查一次,而不是每次迭代),因此此流不是快速失败