【问题标题】:Why the BufferedReader::lines() creates a Stream<T> through Iterator<T> instead of Spliterator<T>?为什么 BufferedReader::lines() 通过 Iterator<T> 而不是 Spliterator<T> 创建 Stream<T>?
【发布时间】:2016-09-25 19:00:08
【问题描述】:

我很惊讶地看到BufferedReader lines() 方法创建了一个Stream&lt;T&gt; 的实例,它实现了Iterator&lt;T&gt; 接口,而不是Spliterator&lt;T&gt;。由于许多原因,使用Spliterator&lt;T&gt; 有几个优点,即使没有并行性。比如Brian Goetz states in its answer对问题Iterator versus Stream of Java 8

Spliterator 的每个元素访问成本从根本上低于 Iterator,即使是按顺序访问也是如此。

那么,为什么BufferedReader::lines() 通过Iterator&lt;T&gt; 创建Stream&lt;T&gt; 而不是Spliterator&lt;T&gt;

【问题讨论】:

  • 人们使用熟悉的东西。 JRE 开发人员没有理由与众不同。
  • 恕我直言,JRE 开发人员正在开发与新 JDK8 API 密切相关的新功能,特别是 Stream&lt;T&gt; 应该意识到这一点。
  • 这也是我的想法,尤其是当谈到如今的 Java 9 新方法时,Spliterator 的优势应该广为人知。不过,我尽量不要判断得太快。我们不知道特定开发人员在实施这种方法时必须处理的工作量……

标签: lambda io java-8 java-stream


【解决方案1】:

使用Iterator 实现它没有技术原因。 Brian Goetz 的声明仍然有效。为什么我怀疑您会注意到这种特定情况下的性能差异,基于Spliterator 的实现会简单得多,因为它所需要的只是一个调用readLine()tryAdvance 方法实现,与迭代器实现相反,它调用readLine()必须保持状态以记住是否已经调用了 hasNext() 以及调用了哪个结果。

所以实际原因是一样的,为什么这里有很多开发人员这样做。迭代器很熟悉,因此开发人员很快就去实现它,知道他们可以包装它(在我之前的答案中我也这样做了)。在 JRE 开发的情况下,可能有历史原因,例如它是在 Spliterator 引入之前实现的,之后才进行重构。

请注意,还有更严重的违规者,例如 String.chars(),它可以实现为快速、轻量级、基于数组的拆分器,并具有完美的并行支持。相反,您将在 Java 8 中获得基于 PrimitiveIterator.OfInt 的实现,该实现更复杂、浪费性能并且本质上具有较差的并行支持(底层实现必须缓冲数据)。

谢天谢地,String.chars() 将在 Java 9 中得到修复,这并不意味着所有相关的开发人员都收到了消息。我刚刚查看了在 Java 9 中引入的Matcher.results(),它还使用了Iterator 绕道(与Scanner.findAll 相比,这是一个正面的反例)。当然,这一切都可能在发布前发生变化。

但 Stream 生成方法中不必要的Iterator 绕道不太可能很快消失。在某些情况下,甚至不值得浪费时间重写方法,一旦它们按照它们的方式实现......

【讨论】:

    猜你喜欢
    • 2012-06-16
    • 2020-09-19
    • 2011-05-12
    • 2020-02-10
    • 2014-10-28
    • 2013-12-06
    • 1970-01-01
    • 2011-06-27
    • 2010-10-14
    相关资源
    最近更新 更多