Concepts / Terminal Operations in Java Streams

Terminal Operations in Java Streams

Filter and map are the two most basic intermediate operations in the Java Stream API, as described in the source.

  • Programming

Predicting a Simple Selection

Stream processing in Java is built from a sequence of values that move through a pipeline. Some operations in that pipeline take a stream in and hand a new stream out. The source material for this article describes these as intermediate operations, because they sit in the middle of the pipeline and change the stream before passing it on. The two most basic intermediate operations it identifies are filter and map.

What do you think happens?

You have a list of numbers from 1 to 6 and want only the even ones. What would you need to tell the stream to do? Is your answer about deciding which values to keep, or about changing the values themselves?

Reveal answer
Answer:

You tell the stream to keep only the values that pass an even-number test. The kept values are 2, 4, and 6, and each one is exactly the value it was in the original list.

Selecting even numbers is a question of deciding which values survive, not of changing them. That is the job of filter. Map answers a different question, what each value should become, and you will meet it shortly.

Filter and the Predicate Test

A filter applies a Predicate and keeps the values for which the predicate is true, without changing them. A predicate is a test that gives a true or false answer for each value. The filter checks every value in the original stream against that test. Values that pass are placed into the new stream, and values that fail are left out.

The important point is that a filter never edits a value. A kept value is exactly the value it was in the original stream. This is why filter is described as a way to narrow down a stream based on a condition. The stream can only shrink or stay the same size through a filter; it never gains new values and never has its values rewritten.

Next valuePredicate testKeep value unchangedLeave value outchecked againsttruefalse
For each value in the stream, the predicate returns true or false. True values are kept in the new stream, and false values are left out. The kept value is unchanged.

A predicate does not build a list and does not return values. It only answers yes or no for the value it is given. The filter is the part that uses those yes or no answers to decide what enters the new stream.

Map and the Function That Produces Outputs

Where a filter asks whether to keep a value, a map asks what to turn it into. A Function is applied to every value in the stream, and the results form the new stream. The source describes map as the operation used to transform the values in a stream.

Unlike filter, the new stream does not generally contain the original values. It contains the outputs of the function. The number of values stays the same, because each input produces exactly one output. A map never drops a value; it replaces every value with whatever the function returns for it.

java
Notes on the code
  • The parameter n is one value from the stream.
  • The body n * n is the output the function produces for that value.
  • The map applies this rule to every value, so the output stream has the same number of values as the input.
Key point

A map changes the contents of the stream: every value is replaced by the output of the function. A filter changes only the number of values, by keeping some and dropping others, and never changes the values it keeps.

Tracing Filter Then Map Together

Filter and map are often combined in one pipeline, with the filter narrowing the values before the map transforms them. The source describes a practical case. Suppose strList is an ArrayList of String, and you want a stream of all the non-null strings in the list, converted to lower case. Selecting the non-null strings is a job for a filter. Converting each one to lower case is a transformation, which is a job for a map.

java
Notes on the code
  • The filter keeps only values that are not null, so the null entry is removed.
  • The map converts each remaining string to lower case.
  • This article describes the pipeline only; it does not cover the operation that finishes a pipeline.
Worked example

Tracing the strList pipeline

Starting from the values Apple, null, and BANANA, which values does each stage produce, and what does the final stream contain?

  1. 1
    Start with three values

    The stream holds Apple, null, and BANANA.

  2. 2
    Apply the filter

    The predicate s != null is true for Apple and BANANA and false for null. Apple and BANANA are kept unchanged; null is left out.

  3. 3
    Apply the map

    Each remaining string passes through toLowerCase. Apple becomes apple and BANANA becomes banana.

  4. 4
    Compare the counts

    The filter reduced three values to two. The map kept the count at two, because each value produced exactly one output.

Result

The final stream contains apple and banana. The filter decided which values survived, and the map decided what each survivor became.

1, 2, 3, 4, 5, 6original streamfilter: evenpredicate2, 4, 6values kept unchangedmap: squarefunction4, 16, 36output stream6 values in3 pass3 values in3 values out
The filter keeps only the even values, leaving 2, 4, and 6 unchanged. The map then squares each kept value, giving 4, 16, and 36. The filter changed how many values there are, and the map changed what each value is.

Filter Versus Map Side by Side

The two operations are easy to confuse because both take a stream in and produce a stream out. The difference lies in what happens to each value. A filter decides whether a value is kept, and a kept value is not altered. A map decides what each value becomes, and every value is replaced by the output of the function.

PredicateValues kept unchangedCount may shrinkFunctionEvery value replacedCount stays the same
Filter takes a predicate and may remove values while leaving kept values untouched. Map takes a function and replaces every value with a new output, keeping the count the same.
QuestionFilterMap
Takes inA PredicateA Function
What it decidesWhether each value is keptWhat each value becomes
Values changed?No, kept values are unchangedYes, every value is replaced by the output
Number of valuesCan stay the same or go downStays the same
Example from this articleKeep only non-null stringsConvert each string to lower case

Filter and map as described in the source material

Mistakes When Reading a Pipeline

  • Common mistake

    Assuming a filter changes the values it keeps

    What it looks like

    Filtering 1 through 6 for evens and expecting 4, 16, and 36 from the filter alone

    Why it fails

    A filter only decides which values survive. The kept values are exactly what they were before.

    Do this instead

    Read the filter as a yes or no test on each value. Squaring the values is the work of a map.

  • Common mistake

    Thinking a map can remove values

    What it looks like

    Expecting a map that converts strings to lower case to drop the null entry

    Why it fails

    A map applies its function to every value and produces one output per input, so the count stays the same.

    Do this instead

    Remove values with a filter placed before the map, as in the strList pipeline.

  • Common mistake

    Treating the predicate as something that builds a list

    What it looks like
    Expecting isPrime(n) to return a list of primes
    Why it fails

    The predicate only answers true or false for one value. The filter gathers the values that pass.

    Do this instead

    Describe the predicate as a per-value test, and describe the filter as the step that collects the passing values.

  • Common mistake

    Reversing the order of filter and map

    Why it fails

    The map would run on values that the filter was meant to remove, and the result can differ.

    Do this instead

    Place the filter first when narrowing is needed before transforming, as the source describes.

Predicting a Two-Stage Result

MEDIUM

A stream holds the integers 1 through 8. The pipeline first keeps only the values greater than 4, then applies a map that doubles each value. Before checking, predict: how many values reach the end, and which values are they?

Hints
  • Start by listing the values the filter keeps: those greater than 4.
  • The map doubles each kept value and does not drop any of them.
  • Count the survivors of the filter first, then check that the map kept the same count.

What do you think happens?

How many values reach the end of the pipeline, and which are they?

Reveal answer
Answer:

Four values reach the end: 10, 12, 14, and 16.

The filter keeps 5, 6, 7, and 8, unchanged. The map doubles each one, giving 10, 12, 14, and 16. The count stays at four through the map because each input produces exactly one output.

Key Takeaways

  • A filter applies a Predicate, a true or false test on each value, and keeps the values that pass without changing them.
  • A map applies a Function to every value and produces one output per input, so the values are transformed and the count stays the same.
  • Filter decides which values survive; map decides what each value becomes. Both take a stream in and produce a stream out.
  • When combined in one pipeline, the filter usually narrows the values first and the map then transforms the survivors.