处理流中的一个元素而不关闭它
Handle one element in a stream without closing it
我正在寻找一种干净高效的方法来将使用者应用于非并行流的一个元素而不关闭流。
我是说我要替换
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
,等等),或者只是将 boolean/test 移动到另一个位置,如函数包装纸。
这可能吗?流的基本模型是否与这种部分读取不兼容?
因为你好像只是在消耗行,所以你可以从中获取Iterator
;注意,下面的代码假定一个非空流:
final Iterator<String> iterator = theStream.iterator();
process(iterator.next());
iterator.forEachRemaining(doSomething());
我认为你所描述的实际上是不可能的。即使是您发布的第一个代码也不是我推荐使用的代码。如果 forEach 以并行方式执行,您的 if(first)
可能无法正常工作。
如果保存数据的 class 是一个集合,您可以使用迭代器获取列表中的第一个。
如果你真的必须使用流,你可以这样做:
// assuming getStreamFromSomewhere recreates the Stream and is not
// very time consuming
getStreamFromSomewhere().limit(1).forEach(doFirst);
getStreamFromSomewhere().skip(1).forEach(doRest);
流是 lazy 所以它实际上不会遍历整个流两次。
重要的是要记住 Stream API 本身不保存任何数据。对 Stream 的任何调用更像是关于如何处理数据以及数据如何从源流向目标的计划。随机访问不是其中的一部分。
不,这是不可能的,因为每次 "final" 操作都会关闭您的流管道。另一种方法如下,使用 Stream
的迭代器。您将只有一个迭代器。我想这就是您真正想要的,因为您坚持只创建一个流。但是,您必须跳过 "functional" 部分。
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); });
我正在寻找一种干净高效的方法来将使用者应用于非并行流的一个元素而不关闭流。
我是说我要替换
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
,等等),或者只是将 boolean/test 移动到另一个位置,如函数包装纸。
这可能吗?流的基本模型是否与这种部分读取不兼容?
因为你好像只是在消耗行,所以你可以从中获取Iterator
;注意,下面的代码假定一个非空流:
final Iterator<String> iterator = theStream.iterator();
process(iterator.next());
iterator.forEachRemaining(doSomething());
我认为你所描述的实际上是不可能的。即使是您发布的第一个代码也不是我推荐使用的代码。如果 forEach 以并行方式执行,您的 if(first)
可能无法正常工作。
如果保存数据的 class 是一个集合,您可以使用迭代器获取列表中的第一个。
如果你真的必须使用流,你可以这样做:
// assuming getStreamFromSomewhere recreates the Stream and is not
// very time consuming
getStreamFromSomewhere().limit(1).forEach(doFirst);
getStreamFromSomewhere().skip(1).forEach(doRest);
流是 lazy 所以它实际上不会遍历整个流两次。
重要的是要记住 Stream API 本身不保存任何数据。对 Stream 的任何调用更像是关于如何处理数据以及数据如何从源流向目标的计划。随机访问不是其中的一部分。
不,这是不可能的,因为每次 "final" 操作都会关闭您的流管道。另一种方法如下,使用 Stream
的迭代器。您将只有一个迭代器。我想这就是您真正想要的,因为您坚持只创建一个流。但是,您必须跳过 "functional" 部分。
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); });