Java中的有限生成流-如何创建一个?
在 Java 中,可以使用 Stream.generate(supplier)
轻松生成无限流.但是,我需要生成一个最终会完成的流.
In Java, one can easily generate an infinite stream with Stream.generate(supplier)
. However, I would need to generate a stream that will eventually finish.
例如,想象一下,我想要一个目录中所有文件的流.文件的数量可能很大,因此我无法预先收集所有数据并从中创建一个流(通过 collection.stream()
).我需要逐个生成序列.但是流显然会在某个时候结束,并且像 (collect()
或 findAny()
) 这样的终端操作符需要处理它,所以 Stream.generate(供应商)
这里不适合.
Imagine, for example, I want a stream of all files in a directory. The number of files can be huge, therefore I can not gather all the data upfront and create a stream from them (via collection.stream()
). I need to generate the sequence piece by piece. But the stream will obviously finish at some point, and terminal operators like (collect()
or findAny()
) need to work on it, so Stream.generate(supplier)
is not suitable here.
在 Java 中是否有任何合理的简单方法可以做到这一点,而无需我自己实现整个 Stream 接口?
Is there any reasonable easy way to do this in Java, without implementing the entire Stream interface on my own?
我可以想到一个简单的 hack - 使用无限的 Stream.generate(supplier)
来实现,并在获取所有实际值时提供 null 或抛出异常.但它会破坏标准的流操作符,我只能将它与我自己知道这种行为的操作符一起使用.
I can think of a simple hack - doing it with infinite Stream.generate(supplier)
, and providing null or throwing an exception when all the actual values are taken. But it would break the standard stream operators, I could use it only with my own operators that are aware of this behaviour.
澄清
评论中的人建议我使用 takeWhile()
运算符.这不是我的意思.如何更好地表达这个问题......我不是在问如何过滤(或限制)现有的流,我是在问如何动态地创建(生成)流,无需预先加载所有元素,但流会有有限大小(事先未知).
People in the comments are proposing me takeWhile()
operator. This is not what I meant. How to phrase the question better... I am not asking how to filter (or limit) an existing stream, I am asking how to create (generate) the stream - dynamically, without loading all the elements upfront, but the stream would have a finite size (unknown in advance).
解决方案
我要找的代码是
Iterator it = myCustomIteratorThatGeneratesTheSequence();
StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), false);
我刚刚研究了java.nio.file.Files
,list(path)
方法是如何实现的.
I just looked into java.nio.file.Files
, how the list(path)
method is implemented.
推荐答案
在 Java 中是否有任何合理的简单方法可以做到这一点,而无需我自己实现整个 Stream 接口?
Is there any reasonable easy way to do this in Java, without implementing the entire Stream interface on my own?
一个简单的 .limit()
保证它会终止.但这并不总是足够强大.
A simple .limit()
guarantees that it will terminate. But that's not always powerful enough.
在 Stream
工厂方法之后,在不重新实现流处理管道的情况下创建自定义流源的最简单方法是继承 java.util.Spliterators.AbstractSpliterator
并传递它到 java.util.stream.StreamSupport.stream(Supplier extends Spliterator
After the Stream
factory methods the simplest approach for creating customs stream sources without reimplementing the stream processing pipeline is subclassing java.util.Spliterators.AbstractSpliterator<T>
and passing it to java.util.stream.StreamSupport.stream(Supplier<? extends Spliterator<T>>, int, boolean)
如果您打算使用并行流,请注意 AbstractSpliterator
只会产生次优拆分.如果您对源代码有更多的控制权,那么完全实现 Spliterator
接口会更好.
If you're intending to use parallel streams note that AbstractSpliterator
only yields suboptimal splitting. If you have more control over your source fully implementing the Spliterator
interface can better.
例如,以下代码段将创建一个 Stream 提供无限序列 1,2,3...
For example, the following snippet would create a Stream providing an infinite sequence 1,2,3...
在该特定示例中,您可以使用 IntStream.range()
in that particular example you could use IntStream.range()
但是流显然会在某个时候结束,像 (collect() 或 findAny()) 这样的终端操作符需要处理它.
But the stream will obviously finish at some point, and terminal operators like (collect() or findAny()) need to work on it.
像 findAny()
这样的短路操作实际上可以在无限流上完成,只要有任何匹配的元素.
short-circuiting operations like findAny()
can actually finish on an infinite stream, as long as there is any element that matches.
Java 9 引入 Stream.iterate 为一些简单的情况生成有限流.
Java 9 introduces Stream.iterate to generate finite streams for some simple cases.
相关文章