为啥没有提供 Stream<E> 的接口?

Posted

技术标签:

【中文标题】为啥没有提供 Stream<E> 的接口?【英文标题】:Why isn't there an Interface for something that provides a Stream<E>?为什么没有提供 Stream<E> 的接口? 【发布时间】:2017-10-09 15:33:30 【问题描述】:

为了防止实现细节泄露,而不是返回,例如,Collection&lt;MyCoolObject&gt;,可以实现Iterable&lt;MyCoolObject&gt;,然后需要从Iterable 接口实现Iterator&lt;T&gt;。因此,无论内部数据结构如何管理,对元素的访问都是通过Iterator

使用 Java 8,可能希望将 Stream&lt;MyCoolObject&gt; stream() 添加到 MyCoolObject。 (另请参阅:Java 8 Lambdas 一书中支持stream 的建议)。虽然添加方法并不困难(我确实阅读了关于Why Iterable Doesn't Provide Stream 的问题),但Java 没有为Streamable&lt;T&gt; 添加接口以反映Iterable&lt;T&gt; 的想法似乎很奇怪。 (好吧,可能因为Streamable 存在一个不同的名称,以便永远使用CORBA 的东西)。

我想我遵循了关于为什么将Stream 添加到Iterable 可能存在问题的答案,但我不明白为什么不能提供Streaming&lt;T&gt; 接口。例如,Collections 可能已经实现了Streaming&lt;T&gt; 接口,它可以让其他对象更清楚地知道我们可以期待stream() 方法。

根据对上述问题的回答,可以通过

Iterable 获得Stream
Stream s = StreamSupport.stream(iter.spliterator(), false);

但这似乎需要做很多工作,因为 MyObject 可能只想实现 stream() 以允许对象的用户这样做

myObject.stream().filter(...).collect(...)

没有来自迭代器的干预转换。

是否有缺少用于流式传输对象的接口的原因?除了在 MyCoolObject 上实现 stream() 并让某人查看 Javadoc 以便他们知道它具有 stream() 方法之外,还有更好的方法吗?

或者,很可能,我对 Stream 的方法有误解吗?

(另外,我在 CoolObject 中实现了stream(),但后来忘记实现parallelStream(),这可以通过拥有接口来缓解)。

【问题讨论】:

如何实现接口比提供方法更清晰?无论哪种方式,您都必须查看 Javadoc。 (当stream().parallel() 做同样的事情时,为什么还要打扰parallelStream()?)您还没有特别清楚地说明您希望interface 添加什么而不是直接添加stream() 方法。 您不需要Streaming 接口来提供stream 方法。 Iterable 支持 for-each 循环。对于流,API 需要采用通用的流生成对象而不是仅采用流吗? @LouisWasserman 在返回对象时进行抽象?这样你就可以说:“这里取这个对象,你可以从中获取一个Stream,但你不需要确切知道它是什么类型的对象” @KevinO 提供一些价值的策略通常由功能接口更好地描述,例如供应商。流提供了iterator() 方法来获取迭代器(仅一次),那么为什么不将策略声明为 Supplier>? 我不会反对一个名字,因为在 CORBA 中有一个同名的类。里面有很多类,很难找到一个不存在的名字。就从Object开始吧…… 【参考方案1】:

这可能会增加任何未来的答案。

我不知道您为什么认为返回 Iterable&lt;MyCoolObject&gt; 而不是 Collection&lt;MyCoolObject&gt; 更好。它可能确实隐藏了细节,并且还会产生更多问题。

一个集合有一个known size,它在拆分并行处理时起着重要作用。这报告为Spliterator.SIZED | Spliterator.SUBSIZED。所以Collection.stream 将比Iterable 更好地处理并行流,它将使用:

public static <T> Spliterator<T> spliteratorUnknownSize 

记录为:

...并实现 trySplit 以允许有限的并行性

这很明显,因为您根本不知道尺寸。 在当前实现下,批量大小为1024。因此,例如,对于任何低于 1024 的元素,您根本不会获得任何并行化。

现在就您的问题而言,在 jdk-8 的早期版本中曾经有这样的事情。它被称为java.util.stream.Streamable。据我所知,它已被删除,因为有些方法返回 Stream,但不是通过 stream() 方法。

String::codePoints()
File::lines
Pattern::splitAsStream
... many others

因此,实现这一点的唯一地方就是集合。据我所知,这将是一个非常孤立的地方。

啊哈时刻

这是负责人的explanation。

这里建议的是删除的原因:

我正在考虑放弃 Streamable 接口。目前唯一 实现者是 Collection,以及所有其他流承载方法 正在提供专门的流(chars()、codePoints()、lines() 等) 使用比“流”更合适的方法名称。所以我认为我们 应该删除 Streamable 并保留 stream() / parallel() 方法 集合(或可能将它们向上移动 Iterable)。

【讨论】:

“它可能确实隐藏了细节......” 除了它出于错误的原因这样做。它对如何调用它做出假设。任何职能都不应该如此自大。您希望返回一个足够通用但也非常有用的类型。返回Iterable 并不是很有用,因此几乎没有任何类 实现Iterable 而不实现Collection 我希望isEmpty() 有一个接口,使用该方法的东西有,但没有。就像没有Streamable 接口一样。太晚了——那艘船已经航行了。 感谢您的好评。 “啊哈时刻”真的很有趣。 很好地挖掘了 Brian Goetz 的评论。除了提供邮件存档的链接之外,我建议您从该电子邮件中提取关键句子【参考方案2】:

Because it was removed,但如果需要,您可以轻松推出自己的 Streamable 实现:

@FunctionalInterface
interface Streamable<T> 
    Stream<T> stream();

如果我们以策略模式为例,您可以这样定义它:

interface StreamStrategy<T> 
    Streamable<T> getStreamable();

这可以基于任何提供返回Stream&lt;T&gt;的方法的支持对象轻松实现,使用方法引用。例如,如果您有一个集合:

class CollectionBasedStrategy<T> implements StreamStrategy<T> 
    @Override
    public Streamable<T> getStreamable() 
        return new ArrayList<T>()::stream;
    

如果Collection 扩展了这样的Streamable 接口,您确实不需要使用方法引用。但除此之外,将其放入 JDK 似乎并没有太多附加价值——如果需要,以后仍然可以添加它。

【讨论】:

以上是关于为啥没有提供 Stream<E> 的接口?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 flatMap 不将 Stream<Stream<SomeClass>> 扁平化到 SomeClass 而是扁平化到 Stream<SomeClass>?

java8新特性-Stream

为啥 Java 8 Stream 上没有直接存在 toList() [重复]

流—Stream

为啥 Stream.CopyTo 不直接写入文件?

Stream数据流