我正在寻找一种干净且高效的方式,将消费者应用于非并行流的一个元素而不关闭流。
我的意思是我要替换
AtomicBoolean firstOneDone = new AtomicBoolean();
lines.forEach(line -> {
if (!firstOneDone.get()) {
// handle first line
firstOneDone.set(true);
} else {
// handle any other line
}
})
有类似的东西
lines.forFirst(header -> {
// handle first line
}).forEach(line -> {
// handle any other line
})
我不想对整个流进行两次传递(复制或重新创建流,peek
等),或者只是将布尔/测试移动到另一个位置,如函数包装器。
这是可能的还是流的基本模型与这种部分阅读不兼容?
答案 0 :(得分:7)
不,这是不可能的,因为每次“最终”操作都会关闭您的流管道。另一种方法是使用Stream
的迭代器。你只有一个迭代器。我想这就是你真正想要的,因为你坚持只创建一个流。但是你必须跳过“功能”部分。
Stream<String> strings = ... ;
Iterator<String> stringsIt = strings.iterator();
if (stringsIt.hasNext()) {
System.out.printf("header: %s%n", stringsIt.next());
while (stringsIt.hasNext()) {
System.out.printf("line: %s%n", stringsIt.next());
}
}
另一种选择,有ZouZou的评论:
Stream<String> strings = ... ;
Iterator<String> stringsIt = strings.iterator();
if (stringsIt.hasNext()) {
System.out.printf("header: %s%n", stringsIt.next());
stringsIt.forEachRemaining(line -> { System.out.printf("line: %s%n", line); });
}
所有功能的最终答案实际上如下:
Stream<String> lines = ... ;
Spliterator<String> linesIt = lines.spliterator();
linesIt.tryAdvance(header -> { System.out.printf("header: %s%n", header); });
linesIt.forEachRemaining(line -> { System.out.printf("line: %s%n", line); });
答案 1 :(得分:1)
由于您似乎只是消耗线条,因此您可以从中抓取Iterator
;请注意,下面的代码假定为非空流:
final Iterator<String> iterator = theStream.iterator();
process(iterator.next());
iterator.forEachRemaining(doSomething());
答案 2 :(得分:1)
我不认为你所描述的内容实际上是可能的。即使您发布的第一个代码也不是我建议使用的代码。如果forEach以并行方式执行,则if(first)
可能无法正常运行。
如果持有数据的类是Collection,您只需使用迭代器获取列表中的第一个。
如果您真的必须使用流,您可以执行以下操作:
// assuming getStreamFromSomewhere recreates the Stream and is not
// very time consuming
getStreamFromSomewhere().limit(1).forEach(doFirst);
getStreamFromSomewhere().skip(1).forEach(doRest);
Streams是lazy所以它实际上不会经历两次整个流。
重要的是要记住,Stream API本身并不包含任何数据。对Stream的任何调用更像是关于如何处理数据以及它如何从源流向目标的计划。随机访问不是其中的一部分。