如何将 Stream 变成 Iterable?

How can I turn a Stream into an Iterable?

Stream inherits an iterator() method to produce an Iterator

但我需要 Iterable rather than an Iterator

例如,给定这个字符串:

String input = "this\n" +
        "that\n" +
        "the_other";

…我需要将字符串的这些部分作为 Iterable 传递给特定的库。调用 input.lines() 会产生 Stream。因此,如果我可以将 Stream 变成其元素的 Iterable,我就会确定。

tl;博士

直接投,无需转换

Stream<String>::iterator 转换为 Iterable<String>

详情

注意 请参阅 Holger 的回答,解释使用流支持的危险 Iterable

是的,你可以做一个Iterable from a Stream

解决方案很简单,但并不明显。参见 this post on Maurice Naftalin's Lambda FAQ

iterator method of BaseStream (superclass of Stream) returning a Iterator matches the only method of the functional interface Iterable的签名,所以方法引用Stream<T>::iterator可以作为Iterable<T>的实例。 (这两种方法同名纯属巧合。)

提出您的意见。

String input = "this\n" +
        "that\n" +
        "the_other";
Stream<String> stream = input.lines() ;

使用方法参考生成一个Iterable<String>

Iterable< String > iterable = stream::iterator;

测试结果。

for ( String s : iterable ) 
{
    System.out.println( "s = " + s );
}

看到这个code run live at IdeOne.com

s = this

s = that

s = the_other

注意事项 谨防流式支持的风险Iterable。解释正确 .

Why does Stream<T> not implement Iterable<T>? 中所述,Iterable 期望能够多次提供 Iterator,而 Stream 无法满足。因此,虽然您可以从 Stream 中创建一个 Iterable 以供临时使用,但您必须注意是否存在多次迭代它的尝试。

既然你说,“我需要将字符串的那些部分作为 Iterable 传递给特定的库”,没有通用的解决方案,因为代码使用Iterable 不在你的控制范围内。

但是如果您是创建流的人,则可以创建一个有效的 Iterable,它会在每次请求 Iterator 时简单地重复流构造:

Iterable<String> lines = () -> "this\nthat\nthe_other".lines().iterator();

这满足了支持任意次数迭代的期望,同时在只遍历一次时不会比单个流消耗更多的资源。

for(var s: lines) System.out.println(s);
lines.forEach(System.out::println);
System.out.println(String.join("\n", lines));