本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看<活动链接>
提问:为什么Stream <T>没有实现Iterable <T>?
在Java 8中,我们有类Stream ,它奇怪地有一个方法
Iterator<T> iterator()
因此,您可能希望它实现接口Iterable ,但事实并非如此。
当我想使用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)) { ... }
我错过了什么知识点吗?
回答1:
主要原因是Iterable具有可重复的语义,而Stream没有。
我认为主要原因是Iterable暗示可重用性,而Stream它只能使用一次,更像是Iterator。
如果Stream扩展了Iterable,则当现有代码第二次接收到Iterable并抛出异常时,现有代码可能会感到惊讶。
回答2:
要将转换Stream为Iterable,您可以执行
Stream<X> stream = null;
Iterable<X> iterable = stream::iterator
要将Stream传递给期望Iterable的方法foo,
void foo(Iterable<X> iterable)
简单的方法:
foo(stream::iterator)
可能更明确一点可能更好
foo( (Iterable<X>)stream::iterator );
回答3:
这么操作可能博采众长:stream中的可重用Iterable满足Iterable规范所提供的所有保证。
注意:SomeType在这里不是类型参数-您需要将其替换为适当的类型(例如String)或使用反射
Stream<SomeType> stream = ...;
Iterable<SomeType> iterable = stream.collect(toList()):
有一个主要的缺点:
延迟迭代的好处将丢失。 如果您计划立即遍历当前线程中的所有值,则所有开销都可以忽略不计。 但是,如果您计划仅部分或在另一个线程中进行迭代,则此立即而完整的迭代可能会产生意想不到的后果。
当然,最大的好处是您可以重用Iterable,而(Iterable )stream :: iterator仅允许一次使用。 如果接收代码将多次遍历集合,则这种方法不仅是必要的,而且可能对性能有好处。