no_joker
no_joker

Reputation: 93

Java 8 Streams peek/map without collecting is not giving output

The below code is not printing any values. None of the System.out.println() statements are getting printed. I assumed peek deals with modifying data. Surprisingly the logic inside the peek() is not even getting executed. If I collect the stream using Collectors I see the desired behavior.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class HelloWorld {
    public static void main(String[] args) {
        List<String> input = new ArrayList<>(Arrays.asList("hello", "bye", "vacation"));
        List<String> output = new ArrayList<>();
        input.stream().peek(x -> {
            System.out.println(x+"inside"); // print statement 1
            if (x.length() > 3) {
                output.add(x.toUpperCase());
            }
        });

        output.forEach(System.out::println); // print statement 2
    }
}

Upvotes: 0

Views: 1780

Answers (3)

Robin
Robin

Reputation: 36601

You lack a terminal operator on your stream, so nothing will happen with it.

See https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html:

Intermediate operations return a new stream. They are always lazy; executing an intermediate operation such as filter() does not actually perform any filtering, but instead creates a new stream that, when traversed, contains the elements of the initial stream that match the given predicate. Traversal of the pipeline source does not begin until the terminal operation of the pipeline is executed.

The .collect is a terminal operator which explains why it works when you replace close with collect.

Upvotes: 0

Nikolas
Nikolas

Reputation: 44378

There are several issues in the code:

  1. The Stream is not closed with a terminal operation, therefore no pipeline operation is invoked.
  2. Stream::peek is an intermediate operation used mainly for debugging.
  3. You should never use any side-effect or stateful operation inside the stream - calling output.add(x.toUpperCase()); is not good.
  4. The output is empty so nothing is printed out as long as the Stream is not closed (see the no. 1).

All you need to understand Stream API is at the package description. Finally, you can conclude into something like:

List<String> output = input.stream()              // Stream<String>
    .peek(x -> System.out.println(x + " inside")) // print each value
    .filter(x -> x.length() > 3)                  // filter Strings longer than 3 chars
    .map(x -> x.toUpperCase())                    // make them uppercase using map
    .collect(Collectors.toList());                // to List - a terminal operation

Upvotes: 3

Jason
Jason

Reputation: 5246

When Stream#peek(Consumer) is referenced, a new StatelessOp object is created from the existing Stream. However, the method from the StatelessOp object called opWrapSink is only called when the Stream elements are consumed. You are not consuming the elements of the Stream at any point. In the documentation the reasoning is that intermediate operations are done lazily.

Laziness-seeking. Many stream operations, such as filtering, mapping, or duplicate removal, can be implemented lazily, exposing opportunities for optimization. For example, "find the first String with three consecutive vowels" need not examine all the input strings. Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy.

https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html

Upvotes: 2

Related Questions