How to convert an iterator to a stream?
我正在寻找一种将Iterator转换为Stream的简明方法,或者更具体地说,将迭代器"查看"为流。
出于性能原因,我希望避免在新列表中使用迭代器的副本:
1 2 3 4
| 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:
1 2 3 4 5
| 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)
1 2 3 4 5 6 7
| 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) |
我看过StreamSupport和Collections,但什么都没找到。
- 如何从迭代器中创建无限流的可能副本?
- @dmitryginzburg euh我不想创建"无限"流。
- @冈塔德,这件事没关系。
- @Dmitryginzburg-Stream.generate(iterator::next)作品?
- @对于有限迭代器不起作用的dmitryginzburg。
- 请参阅stackoverflow.com/questions/23114015/&hellip;
- @布兰戈兹谢谢你的链接
一种方法是从迭代器创建一个拆分器,并将其用作流的基础:
1 2 3 4
| Iterator <String > sourceIterator = Arrays. asList("A", "B", "C"). iterator();
Stream <String > targetStream = StreamSupport. stream(
Spliterators. spliteratorUnknownSize(sourceIterator, Spliterator. ORDERED),
false); |
另一种可能更易读的方法是使用iterable,并且通过迭代器创建iterable对于lambda来说非常容易,因为iterable是一个函数接口:
1 2 3 4
| Iterator <String > sourceIterator = Arrays. asList("A", "B", "C"). iterator();
Iterable <String > iterable = () -> sourceIterator ;
Stream <String > targetStream = StreamSupport. stream(iterable. spliterator(), false); |
号
- 谢谢,它真的是迭代器的view还是副本?
- 流是懒惰的:代码只是将流链接到迭代器,但是实际的迭代直到终端操作才会发生。如果同时使用迭代器,则不会得到预期的结果。例如,您可以在使用流之前引入一个sourceIterator.next(),您将看到效果(流不会看到第一个项目)。
- 对于任何迭代器,Spliterator.ORDERED是否也是有效的字符?
- @skiwi是的,你是对的,实际大小是通过spliteratorUnknownSize方法删除的。我已经修改了。
- @亚西莉亚,是的,真是太好了!也许你可以为将来的读者解释一下这句相当神奇的台词Iterable iterable = () -> sourceIterator;。我不得不承认我花了一些时间才明白。
- @冈塔拉德,这条线对我来说仍然很神奇。你能告诉我你发现了什么吗?它是一个Supplier>吗?
- 我应该告诉你我发现了什么。Iterable是一个FunctionalInterface,它只有一个抽象方法iterator()。因此,() -> sourceIterator是一个lambda表达式,将Iterable实例实例化为匿名实现。
- 同样,() -> sourceIterator;是new Iterable<>() { @Override public Iterator iterator() { return sourceIterator; } }的简称。
- @Jinkwon它并不是匿名类的缩写形式(有一些细微的区别,比如范围和编译方式),但在这种情况下,它的行为是类似的。
- 使用()->sourceinterator的难点在于,如果调用多次,每次都会返回相同的迭代器。这会导致意想不到的结果。因此,您依赖于streamSupport.stream()和spliterator(uufalse)的实现。
- @flup流只能使用一次,所以这不是问题。
- 为了防止重复使用smae迭代器出现上述问题,请使用以下命令:Iterable iterable = Arrays.asList("A","B","C")::iterator; Stream targetStream = StreamSupport.stream(iterable.spliterator(), false);。
- 另请参阅这一行:stackoverflow.com/a/44477890/363573
- @Stephan我认为它不起作用(它将在迭代器末尾抛出异常)。
- @我用arraylist迭代器尝试了assylias,它起作用了。但是,我将arraylist.size()方法传递给了limit()方法。
- @Stephan如果您从集合接收迭代器,它确实有效,但是您可以有一个未知的迭代器?大小,例如stackoverflow.com/a/32232173/829571或stackoverflow.com/a/38990744/829571中的数据库光标
- lists.newarraylist(此处为迭代器).stream())
- 当你在溪流中平行时会发生什么?迭代器必须不是线程安全的才能避免并发问题?
- @我建议您检查Spliterator的JavaDoc。特别是"尽管拆分器在并行算法中有明显的实用性,但不希望它是线程安全的;相反,使用拆分器实现并行算法应确保拆分器一次只能由一个线程使用。"
从版本21开始,guava库提供Streams.stream(iterator)。
这是@assylias的答案所显示的。
- javadoc:static.javadoc.io/com.google.guava/guava/21.0/com/google/com&zwnj;&误8203;mon/&hellip;
- 标记为@beta,所以intellij标记为不稳定…
好建议!这是我的可重复使用版本:
1 2 3 4 5 6 7 8 9 10 11
| 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):
1 2 3
| List<String> aPrefixedStrings = asStream(sourceIterator)
.filter(t -> t.startsWith("A"))
.collect(toList()); |
。
在Java 9中,除了流之外,这是可能的,但是有一个警告要注意。下面的简单示例将无法打印迭代器的最后一个元素。
1 2 3
| Stream. generate(iterator ::next )
. takeWhile(i -> iterator. hasNext())
. forEach(System. out::println ); |
。
但是,这确实可以正常工作:
1 2 3 4
| Stream. generate(() -> null)
. takeWhile(x -> iterator. hasNext())
. map(n -> iterator. next())
. forEach(System. out::println ); |
。
使用Spliterators类从Iterator创建Spliterator包含多个用于创建拆分器的函数,例如这里使用的spliteratorUnknownSize是以迭代器为参数,然后使用StreamSupport创建流。
1 2 3
| Spliterator<Model> spliterator = Spliterators.spliteratorUnknownSize(
iterator, Spliterator.NONNULL);
Stream<Model> stream = StreamSupport.stream(spliterator, false); |
使用Collections.list(iterator).stream()...。
- 虽然很短,但表现非常差。
- 这将将整个迭代器展开为Java对象,然后将其转换为流。我不建议这样做
- 这似乎只是用于枚举,而不是迭代器。
- 一般来说,这不是一个糟糕的答案,在紧要关头有用,但这个问题提到了性能,而答案却没有表现出来。