如何将迭代器转换为流?


468

我正在寻找一种Iterator将a 转换为Stream或更具体地以将“迭代器”作为流“查看”的简洁方法。

出于性能原因,我想避免在新列表中复制迭代器:

Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Collection<String> copyList = new ArrayList<String>();
sourceIterator.forEachRemaining(copyList::add);
Stream<String> targetStream = copyList.stream();

根据评论中的一些建议,我也尝试使用Stream.generate

public static void main(String[] args) throws Exception {
    Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
    Stream<String> targetStream = Stream.generate(sourceIterator::next);
    targetStream.forEach(System.out::println);
}

但是,我得到了NoSuchElementException(因为没有调用hasNext

Exception in thread "main" java.util.NoSuchElementException
    at java.util.AbstractList$Itr.next(AbstractList.java:364)
    at Main$$Lambda$1/1175962212.get(Unknown Source)
    at java.util.stream.StreamSpliterators$InfiniteSupplyingSpliterator$OfRef.tryAdvance(StreamSpliterators.java:1351)
    at java.util.Spliterator.forEachRemaining(Spliterator.java:326)
    at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
    at Main.main(Main.java:20)

我已经看过StreamSupportCollections,但我没有发现任何东西。



3
@DmitryGinzburg嗯,我不想创建“无限”流。
贡塔德2014年

1
@DmitryGinzburg Stream.generate(iterator::next)工作吗?
贡塔德2014年

1
@DmitryGinzburg不适用于有限的迭代器。
assylias 2014年

Answers:


543

一种方法是从迭代器创建一个拆分器,并将其用作流的基础:

Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Stream<String> targetStream = StreamSupport.stream(
          Spliterators.spliteratorUnknownSize(sourceIterator, Spliterator.ORDERED),
          false);

一种可能更易读的替代方法是使用Iterable-使用lambda从Iterator创建Iterable非常容易,因为Iterable是一个功能性接口:

Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();

Iterable<String> iterable = () -> sourceIterator;
Stream<String> targetStream = StreamSupport.stream(iterable.spliterator(), false);

26
Stream是懒惰的:代码仅将Stream链接到Iterator,但实际的迭代直到终端操作才发生。如果在此期间使用迭代器,则不会获得预期的结果。例如,您可以sourceIterator.next()在使用流之前引入,然后您将看到效果(流不会看到第一项)。
assylias 2014年

9
@assylias,是的,非常好!也许您可以为以后的读者解释这条神奇的路线Iterable<String> iterable = () -> sourceIterator;。我不得不承认,我花了一些时间才能理解。
贡塔德2014年

7
我应该告诉我发现的东西。Iterable<T>是一个FunctionalInterface只有一个抽象方法的程序iterator()。因此() -> sourceIterator,lambda表达式将Iterable实例实例化为匿名实现。
Jin Kwon

13
再次,() -> sourceIterator;new Iterable<>() { @Override public Iterator<String> iterator() { return sourceIterator; } }
Jin Kwon

7
@JinKwon实际上,它不是匿名类的简化形式(有一些细微的差别,例如范围和如何编译),但是在这种情况下,它的行为类似。
assylias,2015年

122

从版本21开始,Guava库提供了 Streams.stream(iterator)

它确实执行了@ assylias答案



在JDK支持本机单行代码之前,最好始终使用它。与其他地方显示的纯JDK解决方案相比,将来找到(因此重构)它会容易得多。
drekbour

这非常好,但是…Java如何具有本机迭代器和流…但是没有内置的,直接的方法可以将它们相互转换!我认为这是个很大的疏漏。
Dan Lenski

92

很棒的建议!这是我可重复使用的方法:

public class StreamUtils {

    public static <T> Stream<T> asStream(Iterator<T> sourceIterator) {
        return asStream(sourceIterator, false);
    }

    public static <T> Stream<T> asStream(Iterator<T> sourceIterator, boolean parallel) {
        Iterable<T> iterable = () -> sourceIterator;
        return StreamSupport.stream(iterable.spliterator(), parallel);
    }
}

和用法(确保静态导入asStream):

List<String> aPrefixedStrings = asStream(sourceIterator)
                .filter(t -> t.startsWith("A"))
                .collect(toList());

43

这在Java 9中是可能的。

Stream.generate(() -> null)
    .takeWhile(x -> iterator.hasNext())
    .map(n -> iterator.next())
    .forEach(System.out::println);

1
简单,高效且无需求子类化-这应该是公认的答案!
martyglaubitz

1
不幸的是,这些似乎不适用于.parallel()流。它们似乎比遍历还慢一些Spliterator,即使是顺序使用也是如此。
Thomas Ahle

同样,如果迭代器为空,则第一个方法抛出。第二种方法现在确实可以使用,但是它确实违反了map中的功能要求,并且takeWhile是无状态的,因此我会在生产代码中犹豫。
汉斯·彼得·斯托尔

确实,这应该是一个可以接受的答案。尽管parallel可能很时髦,但简单性却是惊人的。
斯文

11

SpliteratorIterator使用Spliterators类创建类包含不止一个用于创建分隔符的函数,例如,此处使用的spliteratorUnknownSize是将迭代器作为参数,然后使用StreamSupport

Spliterator<Model> spliterator = Spliterators.spliteratorUnknownSize(
        iterator, Spliterator.NONNULL);
Stream<Model> stream = StreamSupport.stream(spliterator, false);

1
import com.google.common.collect.Streams;

并使用Streams.stream(iterator)

Streams.stream(iterator)
       .map(v-> function(v))
       .collect(Collectors.toList());

-4

采用 Collections.list(iterator).stream()...


6
虽然很短,但效果却很差。
奥利维尔·格雷戈尔

2
这会将整个迭代器展开为java对象,然后将其转换为流。我不建议这样做
iec2011007

3
这似乎仅用于枚举,而不是迭代器。
john16384

1
一般而言,这不是一个糟糕的答案,在紧要关头很有用,但是该问题提到了性能,而答案却不是很有效。
雪橇
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.