想象一下,通过回调或InputStream
获得某种传入数据,您需要连续转换为Java 8 Stream
。我们不知道传入的数据流何时停止,但我们知道它可以停止。
到目前为止,我已经看到了解决这个问题的两种方法,我对如何实现这一目标的最佳实践感兴趣。主要是因为我必须是某人以前面对的事情。必须有比下面的想法更简单的方法。
1)最简单的方法是将来源视为Supplier
,并使用Stream.generate
来提供数据:
Stream.generate(() -> blockCallToGetData());
然而,这有一个缺点,即流永远不会结束。因此,只要输入源停止发送,流就会一直调用该方法。除非我们自然地抛出一个Runtime异常,但这可能会变得很难看。
2)第二个想法是使用Iterator
(转换为Spliterator
)next
方法阻塞,直到我们找到下一个元素。作为一个粗略的例子:
class BlockingIterator implements Iterator<Data> {
@Override void boolean hasNext() {
return true;
}
@Override Data next() {
return blockCallToGetData();
}
}
这样做的好处是我可以通过false
方法返回hasNext
来停止流。但是,在我们不控制传入数据速度的情况下(例如在回调中),我们需要为迭代器保留一个就绪元素的缓冲区。在有人在迭代器上调用next
之前,这个缓冲区可能会变得无限大。
所以,我的问题是;将阻塞输入提供给流的最佳做法是什么?
答案 0 :(得分:5)
这个问题包含一个值得怀疑的假设: 是一种将阻塞输入提供给流的好方法。流不是反应性框架;虽然你可以把它变成一个有着大撬棍的人,但问题可能会在其他地方突然出现。 (EG考虑了这些用例,并得出结论,我们最好提供能够在一个问题上完成工作而不是在两个工作中完成一半工作的东西。)
如果您需要反应式框架,最佳做法是使用一个。 RxJava很棒。
答案 1 :(得分:0)
在simple-react中,我们通过使用JDK Streams可以读取的(简单反应)async Queues(JDK队列数据结构上的异步包装)解决了这个问题。如果队列关闭,Stream将自动断开连接。
快速生成者/慢速消费者问题可以通过队列解决。如果(简单响应)异步队列由有界阻塞队列支持,则一旦队列满了,它将自动减慢(阻塞)任何生成线程。
相比之下,LazyFutureStream流实现在内部使用非阻塞队列,如果没有数据存在,甚至会尝试将自己从数据的使用者转移到生产者(因此它可以作为完全无阻塞的流)
使用PushableStreamBuilder的示例:
PushableLazyFutureStream<Integer> pushable = new PushableStreamBuilder()
.withBackPressureAfter(100)
.withBackPressureOn(true)
.pushableLazyFutureStream();
// pushable.getInput().fromStream(input); would also be acceptable to add input data
pushable.getInput().add(100);
pushable.getInput().close();
List list = pushable.getStream().collect(Collectors.toList());
//list is [100]