为什么Stream <T>不能实现Iterable <T>?

在Java 8中,我们有Stream <T>类,它好奇地有一个方法

Iterator<T> iterator() 

所以你会希望它实现接口Iterable <T> ,它需要这个方法,但事实并非如此。

当我想使用foreach循环遍历Stream时,我必须做类似的事情

 public static Iterable<T> getIterable(Stream<T> s) { return new Iterable<T> { @Override public Iterator<T> iterator() { return s.iterator(); } }; } for (T element : getIterable(s)) { ... } 

我在这里错过了什么?

在邮件列表中已经有人问过这个问题了 。 主要原因是Iterable也有一个可重复的语义,而Stream不是。

我认为主要原因是可Iterator意味着可重用性,而Stream只能被使用一次 – 更像Iterator

如果Stream扩展Iterable那么当它接收到一个Iterable时,现有的代码可能会感到惊讶,这个Iterable会在第二次抛出Exception for (element : iterable)时抛出Exception

要将Stream转换为Iterable ,可以这样做

 Stream<X> stream = null; Iterable<X> iterable = stream::iterator 

要将Stream传递给期望Iterable

 void foo(Iterable<X> iterable) 

只是

 foo(stream::iterator) 

然而,它可能看起来有趣。 稍微更清楚一点可能会更好一些

 foo( (Iterable<X>)stream::iterator ); 

我想指出, StreamEx确实实现了Iterable (和Stream )以及Stream缺less的其他非常棒的function。

kennytm描述了为什么将Stream作为Iterable处理是不安全的, Zhongyu 提供了一种解决方法 ,允许在Iterable使用Stream ,尽pipe这是一种不安全的方式。 可以得到两全其美的结果:一个Stream中的可重用Iterable ,满足Iterable规范所做的所有保证。

注意: SomeType在这里不是一个types参数 – 你需要用适当的types(例如String )replace它,或者使用reflection

 Stream<SomeType> stream = ...; Iterable<SomeType> iterable = stream.collect(toList()): 

有一个主要的缺点:

懒惰的迭代的好处将会丢失。 如果您计划立即迭代当前线程中的所有值,则任何开销都可以忽略不计。 但是,如果您计划仅部分迭代或在不同的线程中进行迭代,则可能会产生意想不到的后果。

当然,最大的好处就是可以重用Iterable ,而(Iterable<SomeType>) stream::iterator只允许一次使用。 如果接收代码将迭代多次收集,这不仅是必要的,而且可能有利于性能。

您可以在for循环中使用Stream,如下所示:

 Stream<T> stream = ...; for (T x : (Iterable<T>) stream::iterator) { ... } 

( 在这里运行这个片段 )

(这使用Java 8function接口强制转换。)

(这在上面的一些评论(例如Aleksandr Dubinsky )中已有介绍,但是我想把它提出来,使其更加明显)。

如果你不介意使用第三方库, cyclops-react定义了一个既实现Stream又Iterable并且可重放的Stream(解决kennytm描述的问题)。

  Stream<String> stream = ReactiveSeq.of("hello","world") .map(s->"prefix-"+s); 

要么 :-

  Iterable<String> stream = ReactiveSeq.of("hello","world") .map(s->"prefix-"+s); stream.forEach(System.out::println); stream.forEach(System.out::println); 

[披露我是独眼巨人反应的主要开发者]

不完美,但会工作:

 iterable = stream.collect(Collectors.toList()); 

不完美,因为它会从stream中获取所有项目,并将它们放入该List ,这不完全是IterableStream所关心的。 他们应该是懒惰的 。