Intermediate Operations

An intermediate operation is an operation that allows further operations to be added to a Stream. For example, filter is an intermediate operation since additional operations can be added to a Stream pipeline after filter has been applied to the Stream.

In the examples below, many of the lambdas could be replaced by method references (e.g. the lambda () → new StringBuilder can be replaced by a method reference StringBuilder::new).

Common Operations

The following intermediate operations are accepted by a Stream:

Operation Parameter Returns a Stream that:

filter

Predicate

contains only the elements that match the Predicate

map

Function

contains the results of applying the given Function to the elements of this stream

distinct

-

contains the distinct (i.e. unique) elements in the stream as per the element’s equals() method

sorted

-

contains the elements in the stream in sorted order as per the element’s compareTo() method

sorted

Comparator

contains the elements in the stream in sorted order as per the given Comparator

limit

long

contains the original elements in the stream but truncated to be no longer than the given long value

skip

long

contains the original elements in the stream but after discarding the given long value of elements

flatMap

Function

contains the elements of the Stream`s in this stream obtained by applying the given `Function to the stream elements of this stream

peek

Consumer

contains the original elements in the stream but additionally letting the given Consumer accept each element (side effect)

Below are examples that demonstrate how these operations can be applied.

filter

    Stream.of("B", "A", "C" , "B")
        .filter(s -> s.equals("B"))

returns a Stream with the elements "B" and "B" because only elements that are equal to "B" will pass the filter operation.

map

    Stream.of("B", "A", "C" , "B")
        .map(s -> s.toLowerCase())

returns a Stream with the elements "b", "a", "c" and "b" because each element will be mapped (converted) to its lower case representation.

distinct

    Stream.of("B", "A", "C" , "B")
        .distinct()

returns a Stream with the elements "B", "A" and "C" because only unique elements will pass the distinct operation.

sorted

    Stream.of("B", "A", "C" , "B")
        .sorted()

returns a Stream with the elements "A", "B", "B" and "C" because the sort operation will sort all elements in the stream in natural order.

    Stream.of("B", "A", "C" , "B")
        .sorted(Comparator.reverseOrder())

returns a Stream with the elements "C", "B", "B" and "A" because the sort operation will sort all elements in the stream according to the provided Comparator (in reversed natural order).

limit

    Stream.of("B", "A", "C" , "B")
        .limit(2)

returns a Stream with the elements "B" and "A" because after the two first elements the rest of the elements will be discarded.

skip

    Stream.of("B", "A", "C" , "B")
        .skip(1)

returns a Stream with the elements "A", "C" and "B" because the first element in the stream will be skipped.

flatMap

    Stream.of(
        Stream.of("B", "A"),
        Stream.of("C", "B")
    )
        .flatMap(Function.identity())
        .forEachOrdered(System.out::println);

returns a Stream with the elements "B", "A", "C" and "B" because the two streams (that each contain two elements) are "flattened" to a single Stream with four elements.

    Stream.of(
        Arrays.asList("B", "A"),
        Arrays.asList("C", "B")
    )
        .flatMap(l -> l.stream())

returns a Stream with the elements "B", "A", "C" and "B" because the two lists (that each contain two elements) are "flattened" to a single Stream with four elements. The lists are converted to sub-streams using the List::stream mapper method.

peek

    Stream.of("B", "A", "C" , "B")
        .peek(System.out::print)

returns a Stream with the elements "B", "A", "C" and "B" but, when consumed in its entirety, will print out the text "BACB" as a side effect.

Side-effect usage is discouraged in Streams. Use this operation for debug only.

Stream Property Operations

There are also a number of intermediate operations that controls the properties of the Stream and has no effect on its actual content. These are:

Operation Parameter Returns a Stream that:

parallel

-

is parallel (not sequential)

sequential

-

is sequential (not parallel)

unordered

-

is unordered (data might appear in any order)

onClose

Runnable

will run the provided Runnable when closed

parallel

    Stream.of("B", "A", "C" , "B")
        .parallel()

returns a Stream with the elements "B", "A", "C" and "B" but, when consumed, elements in the Stream may be propagated through the pipeline using different Threads. By default, parallel streams are executed on the default `ForkJoinPool.

sequential

    Stream.of("B", "A", "C" , "B")
        .parallel()
        .sequential()

returns a Stream with the elements "B", "A", "C" and "B" that is not parallel.

unordered

    Stream.of("B", "A", "C" , "B")
        .unordered()

returns a Stream with the given elements but not necessary in any particular order. So when consumed, elements might be encountered in any order, for example in the order "C", "B", "B", "A". Note that unordered is just a relaxation of the stream requirements. Unordered streams can retain their original element order or elements can appear in any other order.

onClose

    Stream.of("B", "A", "C", "B")
        .onClose( () -> System.out.println("The Stream was closed") );

is a Stream with the elements "B", "A", "C" and "B" but, when closed, will print out the text "The Stream was closed".

Map to Primitive Operations

There are also some intermediate operations that maps a Stream to one of the special primitive stream types; IntStrem, LongStream and DoubleStream:

Operation Parameter Returns a Stream that:

mapToInt

ToIntFunction

is an IntStream containing int elements obtained by applying the given ToIntFunction to the elements of this stream

mapToLong

ToLongFunction

is a LongStream containing long elements obtained by applying the given ToLongFunction to the elements of this stream

mapToDouble

ToDoubleFunction

is a DoubleStream containing double elements obtained by applying the given ToDoubleFunction to the elements of this stream

flatMapToInt

Function

contains the int elements of the IntStream`s in this stream obtained by applying the given `Function to the stream elements of this stream

flatMapToLong

Function

contains the long elements of the LongStream`s in this stream obtained by applying the given `Function to the stream elements of this stream

flatMapToDouble

Function

contains the double elements of the DoubleStream`s in this stream obtained by applying the given `Function to the stream elements of this stream

In many cases, primitive streams provide better performance but can only handle streams of: int, long and double.

mapToInt

    Stream.of("B", "A", "C" , "B")
        .mapToInt(s -> s.hashCode())

returns an IntStream with the int elements 66, 65, 67 and 66. (A is 65, B is 66 and so on)

mapToLong

    Stream.of("B", "A", "C", "B")
        .mapToLong(s -> s.hashCode() * 1_000_000_000_000l)

returns a LongStream with the long elements 66000000000000, 65000000000000, 67000000000000 and 66000000000000.

mapToDouble

    Stream.of("B", "A", "C", "B")
        .mapToDouble(s -> s.hashCode() / 10.0)

returns a DoubleStream with the double elements 6.6, 6.5, 6.7 and 6.6. === flatMapToInt

    Stream.of(
        IntStream.of(1, 2),
        IntStream.of(3, 4)
    )
        .flatMapToInt(s -> s.map(i -> i + 1))

returns an IntStream with the int elements 2, 3, 4 and 5 because the two `IntStream`s where flattened to one stream whereby 1 was added to each element.

flatMapToLong

    Stream.of(
        LongStream.of(1, 2),
        LongStream.of(3, 4)
    )
        .flatMapToLong(s -> s.map(i -> i + 1))

returns a LongStream with the long elements 2, 3, 4 and 5 because the two LongStreams where flattened to one stream whereby 1 was added to each element.

flatMapToDouble

    Stream.of(
        DoubleStream.of(1.0, 2.0),
        DoubleStream.of(3.0, 4.0)
    )
        .flatMapToDouble(s -> s.map(i -> i + 1))

returns a DoubleStream with the double elements 2.0, 3.0, 4.0 and 5.0 because the two `DoubleStream`s where flattened to one stream whereby 1 was added to each element.

Primitive Operations

Primitive streams (like IntStream and LongStream) provide similar functionality as ordinary streams but usually the parameter count and types differ so that primitive streams can accept more optimized function variants. Here is a table of some additional Intermediate Operations that primitive Streams can take:

Operation Parameter Returns a Stream that:

boxed

-

contains the boxed elements in the original stream (e.g. an int is boxed to an Integer)

asLongStream

-

contains the elements in the original stream converted to long elements

asDoubleStream

-

contains the elements in the original stream converted to double elements

boxed

    IntStream.of(1, 2, 3, 4)
        .boxed()

returns a Stream with the Integer elements 1, 2, 3 and 4 because the original int elements were boxed to their corresponding Integer elements.

asLongStream

     IntStream.of(1, 2, 3, 4)
        .asLongStream()

returns a LongStream with the long elements 1, 2, 3 and 4 because the original int elements were converted to long elements.

asDoubleStream

    IntStream.of(1, 2, 3, 4)
        .asDoubleStream()

returns a DoubleStream with the double elements 1.0, 2.0, 3.0 and 4.0 because the original int elements were converted to double elements.

Java 9 Operations

Two new intermediate operations were introduced in Java 9. Since these methods were added to the Stream interface with default implementations, these methods can be used by any Stream implementation written in either Java 8 or Java 9.

Operation Parameter Returns a Stream that:

takeWhile

Predicate

contains the elements in the original stream until the the first one fails the Predicate test

dropWhile

Predicate

contains the elements in the original stream dropping all elements until the the first one fails the Predicate test then containing the rest of the elements

takeWhile (Java 9 only)

    Stream.of("B", "A", "C", "B")
        .takeWhile(s -> "B".compareTo(s) >= 0)

returns a Stream with the elements "B" and "A" because when "C" is encountered in the Stream, that element and all following are dropped.

dropWhile (Java 9 only)

    Stream.of("B", "A", "C", "B")
        .dropWhile(s -> "B".compareTo(s) >= 0)

returns a Stream with the elements "C" and "B" because elements are dropped from the Stream but when "C" in encountered, subsequent elements are not dropped.