Process one element in a stream without closing it

I am looking for a clean and efficient way to apply a consumer to one element of a non-parallel stream without closing the stream.

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 similar

 lines.forFirst(header -> { // handle first line }).forEach(line -> { // handle any other line }) 

I don’t want to make two passes throughout the stream (copy or recreate the stream, peek , etc.), or simply move the boolean / test to another place, for example, to the function shell.

Is this possible or is it a fundamental model of threads incompatible with this kind of partial reading?

+6
source share
3 answers

No, this is not possible because your stream pipeline is closed with each "final" operation. An alternative is to use the Stream iterator. You will have only one iterator. I assume that you really want to, as you insist on creating only one thread. However, you will 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()); } } 

Alternative, with ZouZou 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); }); } 

The final answer with all the functionality 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); }); 
+7
source

Since you seem to only consume lines, you can simply take an Iterator from it; note, the code below assumes a non-empty stream:

 final Iterator<String> iterator = theStream.iterator(); process(iterator.next()); iterator.forEachRemaining(doSomething()); 
+1
source

I do not think that what you are describing is actually possible. Even the first code you posted is not what I would recommend using. If forEach runs in parallel, your if(first) may not work correctly.

If the class containing your data is a collection, you can just grab the first one in the list using an iterator.

If you really need 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 will not actually go through the entire stream twice.

It is important to remember that the Stream API does not contain any data. Any call in the stream is more like a plan about what to do with the data and how it flows from the source to the destination. Random access is not part of this.

+1
source

Source: https://habr.com/ru/post/983258/


All Articles