6.2 为什么Stream <T>没有实现Iterable <T>? | Java Debug 笔记

197 阅读2分钟

本文正在参加「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仅允许一次使用。 如果接收代码将多次遍历集合,则这种方法不仅是必要的,而且可能对性能有好处。