I'm looking for a clean and efficient way to apply a consumer to one element of a non parallel stream without closing the stream.
I mean I want to replace
AtomicBoolean firstOneDone = new AtomicBoolean();
lines.forEach(line -> {
if (!firstOneDone.get()) {
// handle first line
firstOneDone.set(true);
} else {
// handle any other line
}
})
with something alike
lines.forFirst(header -> {
// handle first line
}).forEach(line -> {
// handle any other line
})
I don't want to do two passes over the whole stream (copy or recreate the stream, peek
, etc.), or just move the boolean/test to another location like a function wrapper.
Is that possible or is the fundamental model of streams not compatible with this kind of partial reading ?
From the documentation: A stream should be operated on (invoking an intermediate or terminal stream operation) only once. A stream implementation may throw IllegalStateException if it detects that the stream is being reused. So the answer is no, streams are not meant to be reused.
CopyTo(Stream) Reads the bytes from the current stream and writes them to another stream. Both streams positions are advanced by the number of bytes copied.
You should always close a stream in order to free open resources on your OS. Opening a stream always returns an identifier which you can use to close it wherever you are in your code (as long as the identifier is valid), whether their from another method or class.
Java Stream distinct() MethodIf the stream is ordered, the encounter order is preserved. It means that the element occurring first will be present in the distinct elements stream. If the stream is unordered, then the resulting stream elements can be in any order. Stream distinct() is a stateful intermediate operation.
No, it's not possible because your stream pipeline is closed with each "final" operation. An alternative is the following, using the Stream
's iterator. You'll have only one iterator. I guess that's what you actually want since you insist on creating only one stream. However you'll have to skip the "functional" part.
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());
}
}
An alternative, with ZouZou's comments:
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); });
}
A final answer with all functional is actually the following:
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); });
I don't think what you are describing is actually possible. Even the first code you posted is not something I would recommend using. If the forEach gets executed in a parallel way, your if(first)
might not work correctly.
If the class that is holding your data is a Collection you can just grab the first one in the list with the iterator.
If you really have to use streams you can do something like:
// assuming getStreamFromSomewhere recreates the Stream and is not
// very time consuming
getStreamFromSomewhere().limit(1).forEach(doFirst);
getStreamFromSomewhere().skip(1).forEach(doRest);
Streams are lazy so it won't actually go through the whole stream twice.
It is important to remember that the Stream API is not holding any data itself. Any call on a Stream is more like plan about what to do with the data and how it flows from source to destination. Random access is not a part of that.
Since you only seem to be consuming lines, you can just grab the Iterator
from it; note, code below assumes a non empty stream:
final Iterator<String> iterator = theStream.iterator();
process(iterator.next());
iterator.forEachRemaining(doSomething());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With