为啥没有提供 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<MyCoolObject>
,可以实现Iterable<MyCoolObject>
,然后需要从Iterable
接口实现Iterator<T>
。因此,无论内部数据结构如何管理,对元素的访问都是通过Iterator
。
使用 Java 8,可能希望将 Stream<MyCoolObject> stream()
添加到 MyCoolObject
。 (另请参阅:Java 8 Lambdas 一书中支持stream
的建议)。虽然添加方法并不困难(我确实阅读了关于Why Iterable Doesn't Provide Stream 的问题),但Java 没有为Streamable<T>
添加接口以反映Iterable<T>
的想法似乎很奇怪。 (好吧,可能因为Streamable
存在一个不同的名称,以便永远使用CORBA 的东西)。
我想我遵循了关于为什么将Stream
添加到Iterable
可能存在问题的答案,但我不明白为什么不能提供Streaming<T>
接口。例如,Collections
可能已经实现了Streaming<T>
接口,它可以让其他对象更清楚地知道我们可以期待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()
方法来获取迭代器(仅一次),那么为什么不将策略声明为 SupplierObject
开始吧……
【参考方案1】:
这可能会增加任何未来的答案。
我不知道您为什么认为返回 Iterable<MyCoolObject>
而不是 Collection<MyCoolObject>
更好。它可能确实隐藏了细节,并且还会产生更多问题。
一个集合有一个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<T>
的方法的支持对象轻松实现,使用方法引用。例如,如果您有一个集合:
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>?