Concepts / Sequential Streams

Sequential Streams

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

  • Programming

Why a Stream Needs a Starting Point

What do you think happens?

Given a list named names, what kind of stream does names.stream() produce?

  • A sequential stream

  • A parallel stream

  • A collection that is not a stream

Reveal answer
Answer:

A sequential stream

Calling stream() on a collection creates a sequential stream that holds the same values as the collection. The method name tells you which kind of stream you get.

A stream is a way of processing a sequence of values, but it does not appear 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 looking at how a stream is processed, it helps to be precise about how the stream is born from the collection.

The question this article answers is simple to state: when you write a creation call on a collection, how do you know which kind of stream you will get? The answer depends on the method name, and the rest of the article shows how to read it.

Tracing Where Stream Values Come From

A collection serves as the source of values for a stream. This applies to lists, sets, and any other collection. The stream does not need its own storage of values before processing begins; it draws its values from the collection it was created from.

Follow the path of the data in the diagram below. The values flow out of the collection c and into whichever stream the creation call builds. Notice that the collection is the origin in every case, and the creation call decides what kind of stream receives those values.

Collection csource of valuesSequential streamfrom stream()Parallel streamfrom parallelStream()c.stream()c.parallelStream()
How do the elements of a collection flow into the stream created from it, and which call produces which kind of stream?

The diagram makes one point clear. Both calls start from the same collection and draw the same values. The difference lies in the kind of stream each call builds, not in the values it can reach.

Definition

A sequential stream is the stream produced by calling stream() on a collection. It holds the same values as the collection. A parallel stream is the stream produced by calling parallelStream() on a collection, or by calling parallel() on an existing sequential stream.

Naming the Two Creation Calls

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.

The method name is the deciding signal. stream() asks for a sequential stream, and parallelStream() asks for a parallel stream. Keeping these two names straight is the core skill of this article, because the name is the only thing that changes between the two calls.

java
Notes on the code
  • stream() on the collection creates a sequential stream
  • parallelStream() on the same collection creates a parallel stream
  • Both lines use the same source collection, names
Worked example

Predicting the Kind of Stream From a Creation Call

A program has a set named tags. Predict which kind of stream is produced by tags.parallelStream(), and say what the stream holds.

  1. 1
    Identify the source

    The call is made on tags, which is a set. A set is a collection, so it can serve as the source of values for a stream.

  2. 2
    Read the method name

    The method is parallelStream(). The method name determines the kind of stream, and parallelStream() creates a parallel stream.

  3. 3
    Check what the stream holds

    The stream is created from the same collection and holds the same values as tags. Only the kind of stream differs from a stream() call.

Result

tags.parallelStream() produces a parallel stream that holds the same values as the set tags.

Reaching a Parallel Stream in Two Steps

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(), just with one extra step.

Both forms are valid. The two-step form is useful in code that wants the sequential start visible in the source, because the stream() call shows that the stream began as sequential before parallel() changed it.

java
Notes on the code
  • The first line reaches a parallel stream in one call
  • The second line starts sequential with stream(), then calls parallel()
  • Both lines produce a parallel stream from the same collection
c.stream()sequential streamc.parallelStream()parallel streamc.stream().parallel()parallel stream, two steps
Which of stream(), parallelStream(), or parallel() produces a sequential stream, and which produces a parallel one?
Creation formStepsKind of stream produced
c.stream()One call on the collectionSequential
c.parallelStream()One call on the collectionParallel
c.stream().parallel()Two calls: sequential start, then parallel()Parallel

Quick reference for the three creation forms covered in this article

Mistakes When Reading Creation Calls

  • Common mistake

    Assuming that every call to stream() on a collection is parallel

    What it looks like

    names.stream() is written, but the reader expects multiple threads to handle the values.

    Why it fails

    The method name determines the kind of stream. stream() creates a sequential stream.

    Do this instead

    Read the method name first. stream() gives a sequential stream, and parallelStream() gives a parallel one.

  • Common mistake

    Treating stream() and parallelStream() as differing in which values they contain

    What it looks like

    Believing parallelStream() on a set pulls different elements than stream() on the same set.

    Why it fails

    Both calls start from the same collection and draw the same values. The difference is the kind of stream built.

    Do this instead

    Keep the values fixed in your head and change only the stream kind when you compare the two calls.

  • Common mistake

    Thinking parallel() needs a new collection

    What it looks like

    Creating a second list before calling parallel() on the stream.

    Why it fails

    parallel() is called on an existing sequential stream, so the collection is not required to change.

    Do this instead

    Call stream() on the collection, then call parallel() on the resulting stream.

  • Common mistake

    Assuming a set cannot be a stream source because it is not a list

Predicting Stream Kinds in Practice

EASY

A program contains a list named orders. For each of the following expressions, predict whether it produces a sequential stream or a parallel stream: orders.stream(), orders.parallelStream(), and orders.stream().parallel(). Then explain what the three expressions have in common.

Hints
  • Look at the method name first. Which name contains parallel?
  • The two-step form starts with stream(), so think about what parallel() changes.
  • The values drawn from orders are the same in all three cases.

Key Takeaways

  • A collection, such as a list or a set, serves as the source of values for a stream.
  • 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 yields a parallel stream from a sequential start.
  • The method name determines the kind of stream, so reading the creation call tells you how the stream will be processed.