Streams in Java

Java stream API is available in java 8 and is completely different from InputStream and OutputStream of  java I/O. Streams are playing a vital role to bring functional programming to java. In this tutorial you will learn the most powerful operations offered by streams API such as reduce, collect, flatMap and parallel streams. Before starting with stream API, you must have understanding of lambda expressions provided in java and i discussed in “Interfaces in Java” or “functional interfaces” or “collection methods“.

How does stream work?

A stream contains a sequence of elements and allows to perform different kind of operations on these elements.  There are two type of steam operations:

  • Intermediate : return a stream so we can perform multiple operations and are without semicolon. In the example below filter, map and sorted  are intermediate operations.
  • Terminal :  Are void or return non-stream results. Such as forEach is a terminal operation in the example below.

Output

Most of the above stated operations accept some kind of lambda expression parameter using a functional interface to specify the exact behavior of the operation either stateless or non-interfering.

  • Non-Interfering : Does not modify the data source of the stream
  • Stateless : Is a deterministic operation. The state might change during execution.

Streams can be applied on different data structures such as collections, lists and sets. Stream API provides new methods stream() and parallelStream()  to create sequential or parallel stream.

Different types of sequential streams

Using stream method on a list of objects returns a regular stream and we don’t necessarily need to create collections in order to work with streams. Following example is using a stream method.

Use Stream.of() to create a stream of object references.

To work with primitive data type, Streams provides some special operation to work with int, long and double  data types.E.g IntStream, LongStream and DoubleStream.
Using IntStream

Output is 1,2,3,4,5,6,7,8,9
Primitive streams are behaving like regular object streams with additional terminal aggregate operations sum() and average().

Transforming primitive strean to object streams

Output

How stream operations are processed?

Intermediate operation are lazy and will be executed only when a terminal operation is present. The following example doesn’t print anything on console. The reason is terminal operation is missing.

Adding a terminal operation, both terminal and intermediate will be executed.

Output :

The other issue with this operation is that it execute operations one after another on all elements of the stream. To avoid this behavior we can reduce the actual number of operations. In the example below if anyMatch operation returns true as the predicate applies to the given input element. Due to vertical execution of the stream, the operation will return true once element “charlie” passed. In this case map has only to be executed three time instead of mapping all the elements of stream.

Output:

Normally, a stream can’t be reused and stream get closed after a terminal operation. To overcome this limitation, a new stream chain know as stream supplier which can be used for different terminal operations and construct a new stream with all intermediate operations already set up. Follow is an example which allow us to reuse the stream for different terminal operations.

No Responses

Leave a Reply

Your email address will not be published. Required fields are marked *