Javadoc Stream.forEach
表示(强调我的):
此操作的行为明确是不确定的.对于并行流管道,此操作不保证遵守流的遭遇顺序,因为这样做会牺牲并行性的好处.对于任何给定元素,可以在任何时间以及库选择的任何线程中执行该动作.如果操作访问共享状态,则它负责提供所需的同步.
Java 9 Early Access Javadoc中提供了相同的文本.
第一句("明确不确定")表明(但没有明确说明)此方法不会保留遭遇顺序.但是明确说明顺序没有保留的下一个句子是以"For parallel stream pipeline"为条件的,如果不管并行性如何应用该句子,那么这个条件就没有了.这让我不确定forEach是否会保留顺序流的顺序.
这个答案指出了流库实现调用的位置.sequential().forEach(downstream)
.这表明forEach旨在保留顺序流的顺序,但也可能只是库中的错误.
我通过使用forEachOrdered
安全的方式回避了我自己的代码中的这种模糊性,但今天我发现NetBeans IDE的"使用功能操作"编辑器提示将转换
for (Foo foo : collection) foo.bar();
成
collection.stream().forEach((foo) -> { foo.bar(); });
如果forEach不保留遭遇顺序,则会引入错误.在我报告针对NetBeans的错误之前,我想知道库实际上保证了什么,并由源备份.
我正在寻找权威人士的答案.这可能是图书馆实施中的一个明确的评论,关于Java开发邮件列表的讨论(谷歌没有找到任何东西,但我可能不知道这些神奇的词汇),或者是图书馆设计师的声明(其中我知道两个,Brian Goetz和Stuart Marks,积极参与Stack Overflow).(请不要回答"只是使用forEachOrdered" - 我已经这样做了,但我想知道代码是否错误.)
存在用于描述调用者可依赖的最小保证的规范,而不描述实现的功能.这种差距至关重要,因为它允许实施灵活性发展.(规范是声明性的;实施是必要的.)过度规范与指定一样糟糕.
当规范说"不保留属性X"时,并不意味着可能永远不会观察到属性X; 这意味着实施没有义务保留它.您所声称的从未遵守订单的含义只是一个错误的结论.(HashSet
并不保证迭代它的元素会保留它们被插入的顺序,但这并不意味着这不会意外发生 - 你只是不能指望它.)
同样,你暗示"这表明forEach旨在保留顺序流的顺序",因为你看到在某些情况下这样做的实现同样不正确.
在这两种情况下,您似乎对规范提供forEach
了很大的自由这一事实感到不舒服.具体而言,有自由不保留遭遇订单连续流,即使这就是执行目前的确,并且进一步指出,这是一种很难想象一个实现走出去的方式来处理连续源出故障了.但这就是规范所说的,而这就是它的意图.
也就是说,关于并行流的评论的措辞可能令人困惑,因为它仍有可能误解它.这里明确地提出并行案例的意图是教学法; 该规则仍然完全清楚,完全删除了该句子.然而,对于不知道并行性的读者来说,几乎不可能不假设forEach
会保留遭遇顺序,所以添加这句话有助于澄清动机.但是,正如你所指出的那样,特别对待连续案例的愿望仍然是如此强大,以至于进一步澄清是有益的.