Reputation: 20015
I have a Stream that does all processing in peek()
methods. I don't need any result from the Stream, but I need a terminal operation in order for processing to occur. Of course I can terminate the Stream with count()
(or any other operation), but that would be misleading as if I needed some result from Stream termination. What is the correct method to terminate the Stream in such case?
Here is the code for reference:
Stream<Collection<NetworkPart>> graphHolders = cutSegment.stream()
.map(this::obtainCollectionFor);
for (NetworkPart part : edgesToNetworkParts.get(originalSegment)) {
part.integrate(cutSegment);
graphHolders = graphHolders.peek(gh -> gh.add(part));
}
graphHolders.count(); // Just to terminate
Upvotes: 10
Views: 2604
Reputation: 1
Take care when calling "intermediate" stream operations, especially in combination with peek. From the JavaDoc:
"In cases where the stream implementation is able to optimize away the production of some or all the elements (such as with short-circuiting operations like findFirst, or in the example described in count), the action will not be invoked for those elements."
Your "terminating" count() actually prevents the peek from beeing called at all.
Try running this version:
List<AtomicInteger> ints = List.of(new AtomicInteger(0), new AtomicInteger(0));
Stream<AtomicInteger> streamedInts = ints.stream();
for (int i=0; i<5; i++) {
streamedInts = streamedInts.peek(AtomicInteger::incrementAndGet);
}
streamedInts.count();
ints.forEach(foo -> System.out.println(foo.get()));
then replace the ".count()" call to something really terminating like the suggested ".forEach(e -> {})" of @DennisW
But a much better approach would be to use the terminating forEach to actually modify the elements as @gvlasov already suggested.
Note: i would comment another answer, but sadly im not allowed yet :|
Upvotes: 0
Reputation: 20015
This is how I've rewritten the code from the question:
Collection<NetworkPart> partsOwningEdge = edgesToNetworkParts.get(originalSegment);
partsOwningEdge.forEach(part -> part.integrate(cutSegment));
cutSegment.stream()
.map(this::obtainCollectionFor)
.forEach(gh -> gh.addAll(partsOwningEdge));
Upvotes: 5
Reputation: 1057
If it's just the 'misleading' that's irking you, try this:
private static void terminateStream(Stream<?> stream) {
stream.forEach(e -> {});
}
I think this would remove any ambiguity: The method has a clear purpose and the call is clear as well. When used as follows,
public static void main(String[] args) {
Stream<String> stream = Arrays.asList("a", "b", "c").stream();
stream = stream.peek(str -> System.out.println(str));
terminateStream(stream);
}
The main method will print
a
b
c
This does require that stream
is not closed and has not been operated upon, or it will throw an IllegalStateException
.
Upvotes: 3
Reputation: 2689
You could use allMatch() as a terminal operation as this does not need to be evaluated on all elements.
Upvotes: 3