Introduction to Java 8 Streams with example

Java 8 Streams – An Introduction

Stream (not to be confused with InputStream/OutputStream. Both are completely different) is an abstract layer introduced in Java 8. Stream lets you process data in a declarative way. Stream can be seen as an abstraction for expressing efficient, SQL-like operations on a collection of data. Furthermore, streams can process data in parallel, leverage multi-core architectures without you having to write a single line of multithread code. So it completely abstracts out the low level multithreading logic and lets the developer fully concentrate on the data and the operations to be performed on the data.

Before we explore in detail what you can do with streams, let’s take a look at an example using collections.

 

So we have a for loop to iterate through the collection, an if condition to check the name for every iteration. With this code we say what we want (list of employee names that starts with “A”) and also we provide the implementation (loop and if condition) to get the list. What if you want the same list using an SQL query? You would write a query something like,

SELECT name from EMPLOYEE where name=’A%’;

Here we only say what we want. We don’t need to implement how to get the list. It means that you need not worry about how to explicitly implement such queries—it is handled for you. Why can’t we do something similar with collections? Stream is the answer for it.

The same code can be written using streams as follows,

In the above code, we first obtain a stream from the list of employees using the stream() method available on List interface. Next the map() method returns a stream containing list of employee names and then we filter the result using the filter method. The syntax inside the map() and filter() method might look strange if you are not familiar with lambda expressions. I recommend you to read my post on lambda expressions.

What is Stream?

A stream represents a sequence of elements from a source, which supports different kind of operations such as filter, map, reduce, find, match, limit and so on. The source can be collections, arrays, or I/O resources. Streams never store the elements; they are computed on demand. So Streams can be thought of as lazily constructed Collections, where the values are computed when user demands for it.

Unlike Collections, which are iterated explicitly (external iteration), stream operations do the iteration internally for you.

Operations on Streams

There are a variety of operations defined in the Stream interface (java.util.stream.Stream). Look at the below program.

 

Stream operations are either intermediate or terminal. Stream operations that can be connected are called intermediate operations. They can be connected together because their return type is a Stream. In the above examples, the operations map, filter, limit are intermediate operations. All these operations return a Stream so they can be chained to form a pipeline. This is called Pipelining.

Operations that close a stream pipeline are called terminal (end) operations. They collect the results of various stream operations and produce result such as a List, an Integer, or even void (any non-Stream type). In the given example, collect is the terminal operation.

It should be noted that intermediate operations do not perform any processing until a terminal operation is invoked on the stream pipeline. So no work is actually done until collect is invoked. The collect operation will start processing the pipeline to return a result (something that is not a Stream; here, a List). As soon as you call any terminal operation the stream is closed. So streams cannot be reused.

Methods in Stream

We will see some of the methods defined in Stream interface.

forEach

Performs an action for each of the elements in this stream. This is a terminal operation.

map

The map method applies the given function to each element in the stream and returns the result as a stream.

filter

The filter method is used to filter the stream based on the given predicate (condition).

limit

The ‘limit’ method is used to limit the size of the stream.

sorted

This method returns a stream consisting of the elements of this stream, sorted according to natural order. If the elements of this stream are not Comparable, a java.lang.ClassCastException may be thrown when the terminal operation is executed.

distinct

This method returns a stream consisting of the distinct elements (according to Object.equals(Object)) of this stream.

 

Java 8 Streams is a very large concept and lot more can be done with it. I hope this post has given you a basic understanding of the streams which will help you to explore further on the topic.

 

References:

Stream Interface

Processing Data with Java SE 8 Streams

The following two tabs change content below.
Working as a Java developer since 2010. Passionate about programming in Java. I am a part time blogger.

Add Comment

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