top of page

Introduction to Java Streams

What is Stream?

Stream represents a sequence of objects that supports various methods which can be pipelined to produce desired result. Stream does not store data. It operates on source data structures such as List, Collection, and Array etc.

Stream operations types

There are two types of stream operations.

1.     Intermediate operations

Returns a stream that can be chained with other intermediate operations with dot.

2.     Terminal operations

Returns void or non stream output.



Stream pipeline

To perform a sequence of operations over the elements of the data source and aggregate their results, we need three parts: the source, intermediate operation(s) and a terminal operation.

Intermediate operations return a new modified stream.

For example, in below example stream pipeline consists of,  

  • source -> stringList

  • Intermediate operation -> map  

  • Terminal operation -> forEach

Most stream operations accept parameters as that describes user-defined behavior, such as lambda expression map ((s) ->s.toUpperCase()) is passed to map operation.

To get correct behavior, streams parameters should be,

  • non-interfering:  Stream source should not be modified while execution of stream pipeline.

  • Stateless:  In most cases, lambda expressions should be stateless. Its output should not depend on state that might change during execution of Stream pipeline.

Stream Creation

There are multiple ways to create the Stream.

Empty Stream

empty () method can be used to create an empty stream. It is generally used to return Stream with zero elements rather than null.

Collection Stream

Stream can be created from Collection by calling .stream() or .parallelStream() will return you regular object stream.

Stream of()

You don’t need to create collection to get a Stream. You can also use .of()

Stream generate()

generate () method accepts Supplier for element generation. It creates infinite Stream and you can limit it by calling limit () function.

This will create Integer stream with 10 elements with value 1.

Stream iterate()

Stream.iterate() can also be used to generate infinite stream.

The first element of the resulting stream is the first parameter of the iterate() method. When creating every following element, the specified function is applied to the previous element. In the example above the second element will be 42.

Lazy Invocation

Stream intermediate operations are not executed until terminal operation is executed.

Each intermediate operation generates a new stream, stores the provided operation or function. When terminal operation is invoked, stream pipeline execution starts and all the intermediate operations are executed one by one.



In previous output, unless and until terminal operation count is called, nothing was printed on console. Here peek () method is used to print the element of stream. 

Order of operations

In steams order of operations might be surprising.

A common approach will be to perform intermediate operation on all elements and then perform next operation.

In below example,  strings tohfatul and sudha did not go through map and filter operation because we already got findAny result letter starts with ‘r’ that is rupali.



Primitive Streams

Java 8 offers the possibility to create streams out of three primitive types: int, long and double. As Stream<T> is a generic interface, and there is no way to use primitives as a type parameter with generics, three new special interfaces were created: IntStream for int, LongStream for long, DoubleStream for double.

All the primitive Streams are similar to regular Stream with following differences.

  • It supports few terminal aggregate functions such sum ()average (), etc.

  • It accepts specialized function interface such as IntPredicate instead of Predicate, IntConsumer instead of Consumer.


The range(int startInclusive, int endExclusive) method creates an ordered stream from the first parameter to the second parameter. It increments the value of subsequent elements with the step equal to 1. The result doesn’t include the last parameter, it is just an upper bound of the sequence.

The rangeClosed(int startInclusive, int endInclusive) method does the same thing with only one difference, the second element is included. We can use these two methods to generate any of the three types of streams of primitives.

Since Java 8, the Random class provides a wide range of methods for generating streams of primitives. For example, the following code creates a DoubleStream, which has three elements:

Convert Stream to IntStream

You may need to convert Stream to IntStream to perform terminal aggregate operations such as sum or average. You can use mapToInt()mapToLong() or mapToDouble() method to convert Stream to primitive Streams.


Convert IntStream to Stream

You may need to convert IntStream to Stream to use it as any other datatype. You can use mapToObj() convert primitive Streams to regular Stream.


Parallel streams

Java 8 introduced a way of accomplishing parallelism in a functional style using streams.

The API allows us to create parallel streams, which perform operations in a parallel mode. When the source of a stream is a Collection or an array, it can be achieved with the help of the parallelStream() method


If the source of a stream is something other than a Collection or an array, the parallel() method should be used:


When using streams in parallel mode, avoid blocking operations. It is also best to use parallel mode when tasks need a similar amount of time to execute. If one task lasts much longer than the other, it can slow down the complete app’s workflow

The stream in parallel mode can be converted back to the sequential mode by using the sequential() method:


Intermediate Operations

Let's understand the Stream intermediate operations with an example.

  • filter()

  • map()

  • flatMap()

  • distinct()

  • sorted()

  • peek()

  • limit()

  • skip()


The Java Stream filter() can be used to filter out elements from a Java Stream. The filter method takes a Predicate that is called for each element in the stream. If the element is to be included in the resulting Stream, the Predicate should return true. If the element should not be included, the Predicate should return false.



Using filter() method to filter List of string objects:


The Java Stream map() method converts (maps) an element to another object. For instance, if you had a list of strings it could convert each string to lowercase, uppercase, or to a substring of the original string, or something completely else. 




The Stream.flatMap() function, as the name suggests, is the combination of a map and a flat operation. This means you first apply the map function and then flatten the result.

To understand what flattening a stream consists in, consider a structure like [ [1,2,3],[4,5,6],[7,8,9] ] which has "two levels". It's basically a big List containing three more List. Flattening this means transforming it in a "one level" structure e.g. [ 1,2,3,4,5,6,7,8,9 ] i.e. just one list.




The Java Stream distinct() method is a non-terminal operation that returns a new Stream that will only contain the distinct elements from the original stream. Any duplicates will be eliminated.




The Java Stream limit() method can limit the number of elements in a stream to a number given to the limit() method as a parameter. The limit() method returns a new Stream that will at most contain the given number of elements.




The Java Stream peek() method is a non-terminal operation that takes a Consumer interface as a parameter. The Consumer will get called for each element in the stream. The peek() method returns a new Stream that contains all the elements in the original stream.

The purpose of the peek() method is, as the method says, to peek at the elements in the stream, not to transform them. Keep in mind that the peek method does not start the internal iteration of the elements in the stream. You need to call a terminal operation for that.



As peek()‘s method: “This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline“.

Terminal Stream operations:

  • anyMatch()

  • allMatch()

  • noneMatch()

  • collect()

  • count()

  • findAny()

  • findFirst()

  • forEach()

  • min()

  • max()

  • reduce()

  • toArray()


The Java Stream anyMatch() method is a terminal operation that takes a single Predicate as a parameter, starts the internal iteration of the Stream, and applies the Predicate parameter to each element.




The Java Stream allMatch() method is a terminal operation that takes a single Predicate as the parameter, starts the internal iteration of elements in the Stream, and applies the Predicate parameter to each element.

If the Predicate returns true for all elements in the Stream, the allMatch() will return true. If not all elements match the Predicate, the allMatch() method returns false.




The Java Stream noneMatch() method is a terminal operation that will iterate the elements in the stream and return true or false, depending on whether no elements in the stream match the Predicate passed to noneMatch() as the parameter.

The noneMatch() method will return true if no elements are matched by the Predicate, and false if one or more elements are matched.




The Java Stream collect() method is a terminal operation that starts the internal iteration of elements and collects the elements in the stream in a collection or object of some kind.




The Java Stream count() method is a terminal operation that starts the internal iteration of the elements in the Stream and counts the elements.




The Java Stream findAny() method can find a single element from the Stream. The element found can be from anywhere in the Stream. There is no guarantee about from where in the stream the element is taken.




The Java Stream findFirst() method finds the first element in the Stream if any elements are present in the Stream. The findFirst() method returns an Optional from which you can obtain the element if present.




The Java Stream forEach() method is a terminal operation that starts the internal iteration of the elements in the Stream and applies a Consumer (java.util.function.Consumer) to each element in the Stream. The forEach() method returns void.




The Java Stream min() method is a terminal operation that returns the smallest element in the Stream.




The Java Stream max() method is a terminal operation that returns the largest element in the Stream.




The Java Stream toArray() method is a terminal operation that starts the internal iteration of the elements in the stream and returns an array of Objects containing all the elements.




The Stream API is a powerful, but simple to understand set of tools for processing the sequence of elements. When used properly, it allows us to reduce a huge amount of boilerplate code, create more readable programs, and improve an app’s productivity.

In most of the code samples shown in this article, we left the streams unconsumed (we didn’t apply the close() method or a terminal operation). In a real java application, please don’t leave an instantiated stream unconsumed, as that will lead to memory leaks.

137 views0 comments

Recent Posts

See All


Rated 0 out of 5 stars.
No ratings yet

Add a rating
bottom of page