Concepts / Parallel Streams

Parallel Streams

A collection serves as the source of values for a stream, and this applies to lists, sets, and any other collection.

  • Programming

Why the Creation Call Matters

A stream is a way of processing a sequence of values, but a stream never appears out of nowhere. Every stream needs a starting point that supplies the values it will work on. In this article that starting point is a collection, such as a list or a set. Before you think about how a stream is processed, you need to be precise about how the stream is born from the collection, because the call you use to create it decides what kind of stream you get.

What do you think happens?

Given a list named c, which kind of stream does c.stream() produce?

  • A sequential stream

  • A parallel stream

Reveal answer
Answer:

A sequential stream

Calling c.stream() on a collection produces a sequential stream holding the same values as the collection. The name stream() asks for a sequential stream, while parallelStream() asks for a parallel one.

Tracing Values From a Collection Into a Stream

Think of the collection as the source and the stream as the channel that carries its values. The collection holds the values; the stream is created from it and delivers those same values for processing. The same source can feed either kind of stream. What changes between one creation call and another is not the collection, which stays the same, but the kind of stream that comes out of it.

This works the same way for lists and sets. You do not need a different technique for each type of collection. If you know the creation call, you already know which kind of stream the collection will feed.

Two Direct Ways to Create a Stream

Given a collection named c, there are two direct ways to obtain a stream from it. Calling c.stream() produces a sequential stream that holds the same values as the collection. Calling c.parallelStream() produces a parallel stream from the same collection. Both calls start from the same collection and draw the same values. The difference lies only in the kind of stream each call builds.

java
Output
4
4
Notes on the code
  • numbers is the collection that supplies the values
  • stream() asks for a sequential stream from numbers
  • parallelStream() asks for a parallel stream from the same numbers
  • The filter and count steps are identical in both pipelines; only the creation call differs

In the example, the two pipelines differ only in the first call after numbers. Both count the even values in the list, which are 4, 8, 16, and 42, so both print 4. Reading the names tells you the kind of stream: stream() gives a sequential stream and parallelStream() gives a parallel one.

Deriving a Parallel Stream With parallel()

There is a second route to a parallel stream. You can first create a sequential stream with stream() and then call parallel() on it. The result is a parallel stream, so this route reaches the same destination as parallelStream(), with one extra step. Both forms are valid. You will see the stream() plus parallel() form in code that wants the sequential start visible in the source.

java
Output
4
Notes on the code
  • stream() creates a sequential stream from numbers
  • parallel() changes that stream into a parallel stream
  • The filter and count operations are unchanged by the switch

The diagram traces the derived route. The list supplies the values, stream() creates a sequential stream from it, and parallel() changes that stream's mode. The source collection is unchanged by the extra call, and the values draw from the same collection. Only the mode of the stream changes, which is why the result is a parallel stream.

How a Parallel Stream Handles Its Values

A parallel stream is designed to process its values in parallel rather than one step after another. The diagram below gives a high-level picture of this. The source pack for this article does not describe the internal splitting or recombination in detail, so treat the figure as an overview of the idea rather than a precise account of how the work is divided.

Mistakes Beginners Make With Creation Calls

  • Common mistake

    Assuming stream() creates a parallel stream because the collection is large

    What it looks like
    List<Integer> big = ...; big.stream() is treated as parallel
    Why it fails

    The method name determines the kind of stream. stream() always asks for a sequential stream.

    Do this instead

    Use parallelStream() or add parallel() if you want a parallel stream.

  • Common mistake

    Thinking parallelStream() and stream() draw different values

    Why it fails

    Both start from the same collection and draw the same values. Only the kind of stream differs.

    Do this instead

    Compare the creation calls by name, not by the data they hold.

  • Common mistake

    Believing parallel() changes the collection itself

    Why it fails

    parallel() is called on a stream and changes the stream's mode. The collection that supplied the values is not altered by this call.

    Do this instead

    Track the stream, not the collection, when you read a chain that includes parallel().

  • Common mistake

    Forgetting that parallelStream() and stream().parallel() reach the same kind of stream

Comparing the Three Creation Forms

Creation callStarting pointKind of stream producedRoute
c.stream()Collection cSequentialOne direct call
c.parallelStream()Collection cParallelOne direct call
c.stream().parallel()Collection c, then a sequential streamParallelTwo steps

The three creation forms for a stream built from a collection named c

Predicting the Stream Type

EASY

Given a list named names, predict which kind of stream each of these calls produces: names.stream(), names.parallelStream(), and names.stream().parallel(). Then state which call takes a sequential start and adds one step to become parallel.

Hints
  • The method name tells you which kind of stream you are asking for
  • stream() on its own gives a sequential stream
  • parallel() is called on a stream, after the stream has been created

Key Takeaways

  • A collection is the source of values for a stream, and the same idea applies to lists, sets, and other collections.
  • c.stream() creates a sequential stream, and c.parallelStream() creates a parallel stream from the same collection.
  • c.stream().parallel() is a two-step route that also produces a parallel stream from a sequential start.
  • The method name determines the kind of stream, so reading the creation call tells you how it will be processed.