gvlasov
gvlasov

Reputation: 20015

How can I terminate a Stream if I don't need any value from termination?

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

Answers (4)

BuzzDee
BuzzDee

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

gvlasov
gvlasov

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

DennisW
DennisW

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

Neil Masson
Neil Masson

Reputation: 2689

You could use allMatch() as a terminal operation as this does not need to be evaluated on all elements.

Upvotes: 3

Related Questions