297

在 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)) { ... }

我在这里错过了什么吗?

4

9 回答 9

221

人们已经在邮件列表中提出了同样的问题☺。主要原因是 Iterable 也具有可重复迭代的语义,而 Stream 则没有。

我认为主要原因是这Iterable意味着可重用性,而Stream它只能使用一次——更像是一个Iterator.

如果Stream扩展,那么现有代码在收到第二次抛出的 anIterable时可能会感到惊讶。IterableExceptionfor (element : iterable)

于 2013-11-21T19:26:45.983 回答
176

要将 a 转换为 a StreamIterable您可以执行

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

将 a 传递Stream给期望的方法Iterable

void foo(Iterable<X> iterable)

简单地

foo(stream::iterator) 

但是它可能看起来很有趣;更明确一点可能会更好

foo( (Iterable<X>)stream::iterator );
于 2013-11-21T19:46:29.087 回答
11

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

Stream<T> stream = ...;

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

(在此处运行此代码段

(这使用 Java 8 功能接口转换。)

(上面的一些评论(例如Aleksandr Dubinsky)对此进行了介绍,但我想将其拉出到答案中以使其更加明显。)

于 2017-06-27T10:09:15.340 回答
10

我想指出它StreamEx确实实现了Iterable(和Stream),以及Stream.

于 2015-05-30T16:16:47.027 回答
7

kennytm 描述了为什么将 aStream视为 a 是不安全的IterableZhong Yu 提供了一种解决方法,允许使用 a Streamas in Iterable,尽管是以不安全的方式。两全其美是可能的:IterableStream满足Iterable规范做出的所有保证的 a 中可重用。

注意:SomeType这里不是类型参数——您需要将其替换为适当的类型(例如,String)或诉诸反射

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

有一个主要缺点:

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

当然,最大的优势是您可以重复使用Iterable,而(Iterable<SomeType>) stream::iterator只允许一次使用。如果接收代码将多次迭代集合,这不仅是必要的,而且可能对性能有益。

于 2016-01-22T08:15:08.213 回答
3

Stream不执行Iterable。对的一般理解Iterable是可以反复迭代的任何东西。Stream可能无法重播。

我能想到的唯一解决方法是重新创建流,基于流的迭代也是可重放的。每次创建一个新的迭代器时,我都使用Supplier下面的方法来创建一个新的流实例。

    Supplier<Stream<Integer>> streamSupplier = () -> Stream.of(10);
    Iterable<Integer> iterable = () -> streamSupplier.get().iterator();
    for(int i : iterable) {
        System.out.println(i);
    }
    // Can iterate again
    for(int i : iterable) {
        System.out.println(i);
    }
于 2018-04-21T05:40:24.960 回答
2

如果您不介意使用第三方库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);

[披露我是 cyclops-react 的主要开发者]

于 2017-03-10T13:38:07.740 回答
0

不完美,但会起作用:

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

并不完美,因为它会从流中获取所有项目并将它们放入 that 中List,这并不是真正IterableStream内容。他们应该是懒惰的。

于 2017-01-07T04:47:48.620 回答
-1

您可以使用以下方法遍历文件夹中的所有文件Stream<Path>

Path path = Paths.get("...");
Stream<Path> files = Files.list(path);

for (Iterator<Path> it = files.iterator(); it.hasNext(); )
{
    Object file = it.next();

    // ...
}
于 2017-12-23T21:12:37.393 回答