使用Java 8 JDK将Iterable转换为Stream

510

我有一个返回java.lang.Iterable<T>的接口。

我想使用Java 8 Stream API操纵该结果。

然而,Iterable不能"stream"。

有没有办法在不将其转换为List的情况下将Iterable用作Stream?


2
如果您可以迭代,为什么不直接使用循环来检查其条件、值或其他内容呢? - Afzaal Ahmad Zeeshan
34
@AfzaalAhmadZeeshan 因为流更好。 - Sleiman Jneidi
2
正如我所说,我需要对那个列表进行一些操作(过滤、映射)。我想使用新的Java 8 JDK API -> Stream。但是Iterable不是“SteamAble”。 - rayman
1
看起来很奇怪,myIterable.stream() 不存在! - Josh M.
在2018年,我们也有Stream.of(iterable)。 - Guillaume
14
是的,但是Stream.of(iterable)会生成Stream<Iterable<Object>> - Matthias Braun
8个回答

695

有比直接使用spliteratorUnknownSize更好的解决方案,既更容易实现,又能获得更好的结果。 Iterable具有spliterator()方法,因此你应该使用它来获取你的spliterator。最坏的情况下,它是相同的代码(默认实现使用spliteratorUnknownSize),但在更常见的情况下,如果你的Iterable已经是一个集合,则会获得更好的spliterator,因此可以获得更好的流性能(甚至可能有良好的并行性)。而且,这还需要更少的代码:

StreamSupport.stream(iterable.spliterator(), false)
             .filter(...)
             .moreStreamOps(...);

正如您所看到的,从Iterable获得一个流(也可以参见这个问题)并不困难。


139
Stream 上的静态方法会很不错,例如 Stream.ofIterable(iterable) - robinst
89
这并非疏忽;这是一个经过深思熟虑(并且备受争议)的选择。挑战在于,如果存在这样的方法,那么很容易在不了解相关权衡的情况下使用它。因为Iterable是如此抽象,这样的方法将导致性能最差的流(没有并行支持、没有大小信息或特性(用于优化执行选择))。强制更多的思考会产生整个生态系统中更好的API。这是"对于XYZ代码最好"与"对于所有Java代码最好"之间的权衡。 - Brian Goetz
3
根据您的解释,我很好奇为什么我们有了Collection.stream()而没有Iterable.stream()。似乎省略Iterable.stream()(或Stream.ofIterable())的原因同样适用于Collection.stream()。 - qualidafial
6
@qualidafial 请访问 https://dev59.com/TWAg5IYBdhLWcg3w1t9a。需要翻译的内容已经是英文了,无需再次翻译。 - Brian Goetz
14
@BrianGoetz,看起来大多数人还没有达到理解您上面所说的水平,或者他们不关心。他们只想通过调用简单的API来编写简单的代码。另一方面,那些内容(例如并发性)可能对于大多数日常可迭代操作并不重要。 - user_3380739
显示剩余2条评论

95

如果您可以使用Guava库,自版本21以来,您可以使用

Streams.stream(iterable)

3
如果你卡在旧版本上了,可以使用Lists.newArrayList(Iterable) - Jacob van Lingen
1
当前的Guava实现并不比接受的答案差:https://github.com/google/guava/blob/master/guava/src/com/google/common/collect/Streams.java#L60 - Vadzim

24

您可以轻松地通过一个 IterableIterator 创建一个 Stream:

public static <T> Stream<T> stream(Iterable<T> iterable) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            iterable.iterator(),
            Spliterator.ORDERED
        ),
        false
    );
}

2
你只需要写一次这个函数,然后就可以直接调用它。为什么调用stream(...)会让你的代码变得混乱呢? - gexicide
1
想要以简洁优雅的方式内联完成它。你说得对,我可以写一次这个函数,但我更喜欢删除代码(而不是添加代码)。无论如何,这个答案是正确的,因为这是转换的方法。 - rayman
静态导入该函数。简短而优雅。(虽然不一定透明) - aepurniet

11

我建议使用JOOL库,它将spliterator技术隐藏在Seq.seq(iterable)调用后面,并提供了大量其他有用的功能。


哇,原来有整个 JOO* 家族啊。我只熟悉 JOOQ。谢谢! - Pawel Zieminski

8

正如其他回答提到的,Guava可以通过使用以下方式支持此功能:

Streams.stream(iterable);

我想强调的是,这个实现与其他答案提供的略有不同。如果IterableCollection类型,它们会进行强制转换。

public static <T> Stream<T> stream(Iterable<T> iterable) {
  return (iterable instanceof Collection)
    ? ((Collection<T>) iterable).stream()
    : StreamSupport.stream(iterable.spliterator(), false);
}

public static <T> Stream<T> stream(Iterator<T> iterator) {
  return StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(iterator, 0),
    false
  );
}

6
我已经创建了这个类:
public class Streams {
    /**
     * Converts Iterable to stream
     */
    public static <T> Stream<T>  streamOf(final Iterable<T> iterable) {
        return toStream(iterable, false);
    }

    /**
     * Converts Iterable to parallel stream
     */
    public static <T> Stream<T> parallelStreamOf(final Iterable<T> iterable) {
        return toStream(iterable, true);
    }

    private static <T> Stream<T> toStream(final Iterable<T> iterable, final boolean isParallel) {
        return StreamSupport.stream(iterable.spliterator(), isParallel);
    }
}

我认为这段代码非常易读,因为你不需要考虑分割器和布尔值(isParallel)。


4
一个非常简单的解决方法是创建一个 Streamable<T> 接口,该接口扩展了 Iterable<T>,并包含一个 default <T> stream() 方法。
interface Streamable<T> extends Iterable<T> {
    default Stream<T> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
}

现在,您可以通过将其声明为implements Streamable<T>而不是Iterable<T>,轻松地使任何Iterable<T>可流化。

0
如果您使用Vavr(以前称为Javaslang),那么这将非常容易:
Iterable i = //...
Stream.ofAll(i);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接