在Java中,使用Stream.generate(supplier)
可以很容易地生成一个无限流,但是,我需要生成一个最终会完成的流。
想象一下,例如,我想要一个目录中所有文件的流,文件的数量可能很大,因此我无法预先收集所有数据并从中创建流(通过collection.stream()
)。我需要一段一段地生成序列。但是流显然会在某个时候结束,终端操作员喜欢(collect()
或findAny()
)需要在上面工作,所以Stream.generate(supplier)
在这里不合适。
在Java中,有没有什么合理的简单方法可以做到这一点,而不必自己实现整个Stream接口?
我可以想到一个简单的方法--用无限的Stream.generate(supplier)
来做,当所有的实际值都被取走时,提供null或者抛出一个异常。但是这会破坏标准的流操作符,我只能用我自己的操作符来使用它,这些操作符知道这种行为。
- 澄清**
评论中的人建议我使用takeWhile()
操作符。这不是我的意思。如何更好地表达这个问题...我不是在问如何过滤(或限制)现有的流,我是在问如何动态地创建(生成)流,而不是预先加载所有元素,但流将具有有限的大小(事先未知)。
- 解决方案**
我要找的密码是
Iterator it = myCustomIteratorThatGeneratesTheSequence();
StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), false);
我刚刚研究了java.nio.file.Files
,list(path)
方法是如何实现的。
4条答案
按热度按时间kmpatx3s1#
虽然作者已经放弃了
takeWhile
选项,但我发现它对于某些用例来说已经足够了,值得解释一下。方法
takeWhile
可用于任何流,并将在提供给该方法的 predicate 返回false
时终止该流。导致false
的对象不会追加到该流;只有产生true
的对象才被向下传递。因此,生成有限流的一种方法是使用
Stream.generate
方法,并返回一个值,该值通过提供给takeWhile
的 predicate 计算为false来表示流的结束。下面是一个例子,生成数组的所有排列:
在这个例子中,我使用了
null
值来表示流的结束,方法的调用者不会收到空值!OP可以使用类似的策略,并将其与访问者模式相结合。
如果它是一个平面目录,OP最好使用
Stream.iterate
,其中seed是要生成的文件的索引,Stream.limit
是文件的数量(不需要浏览目录就可以知道)。svmlkihl2#
在Java中,有没有什么合理的简单方法可以做到这一点,而不必自己实现整个Stream接口?
一个简单的
.limit()
就可以保证它会终止,但这并不总是足够强大。在
Stream
工厂方法之后,创建定制流源而不重新实现流处理管道的最简单方法是将java.util.Spliterators.AbstractSpliterator<T>
子类化并将其传递给java.util.stream.StreamSupport.stream(Supplier<? extends Spliterator<T>>, int, boolean)
如果你打算使用并行流,注意
AbstractSpliterator
只能产生次优的拆分,如果你对你的源代码有更多的控制,完全实现Spliterator
接口会更好。例如,下面的代码片段将创建一个Stream,提供一个无限序列1,2,3 ...
在特定示例中,您可以使用
IntStream.range()
但是流显然会在某个点结束,像(collect()或findAny())这样的终端操作符需要对其进行处理。
像
findAny()
这样的短路操作实际上可以在无限流上完成,只要存在任何匹配的元素。Java 9引入了Stream. iterate来为一些简单的情况生成有限流。
8ehkhllq3#
用于从InputStream创建JsonNode流的Kotlin代码
bjg7j2ky4#
下面是一个定制的、有限的流: