Of the many new features recently added to Java, two of the most important are lambda expressions and the stream API. A stream is a conduit for data. Thus, a stream represents a sequence of objects. A stream operates on a data source, such as an array or a collection. A stream, itself, never provides storage for the data. It simply moves data, possibly filtering, sorting, or otherwise operating on that data in the process. As a general rule, however, a stream operation by itself does not modify the data source. For example, sorting a stream does not change the order of the source. Rather, sorting a stream results in the creation of a new stream that produces the sorted result.


Stream and Lambda

The stream API is designed with lambda expressions in mind. Moreover, the stream API provides some of the most significant demonstrations of the power that lambdas bring to Java. Although its design compatibility with lambda expressions is impressive, the key aspect of the stream API is its ability to perform very sophisticated operations that search, filter, map, or otherwise manipulate data. Put simply, the stream API provides a powerful means of handling data in an efficient, yet easy to use way. The stream API uses some of Java’s most advanced features. To fully understand and utilize it requires a solid understanding of generics and lambda expressions.

Stream Interfaces

The stream API defines several stream interfaces, which are packaged in At the foundation is BaseStream, which defines the basic functionality available in all streams. BaseStream are derived several types of stream interfaces. The most general of these is Stream:


Terminal and Intermediate Operations

Many methods of the BaseStream and Stream are notated as being either terminal or intermediate. A stream produce only one type operation at a time, either teminal or intermideate. The difference between the two is very important. A terminal operation disconnects the source from the stream when its operation is completed. Terminal operation is used to produce a result, such as finding the minimum value in the stream, or to execute some action, as is the case with the forEach() method. Once a terminal operation of the stream has been executed, it cannot be reused. Intermediate operations produce another stream. Thus, intermediate operations can be used to create a pipeline that performs a sequence of actions:


One other point: intermediate operations do not take place immediately. Instead, the specified action is performed when a terminal operation is executed on the new stream created by an intermediate operation:


This mechanism is referred to as lazy behavior, and the intermediate operations are referred to as lazy. The use of lazy behavior enables the stream API to perform more efficiently.

Obtaining a Stream

You can obtain a stream in a number of ways. Perhaps the most common is when a stream is obtained for a collection. Beginning with JDK 8, the Collection interface was expanded to include the methods that obtain a stream from a collection. The one of the methods is stream( ). Its default implementation returns a sequential stream: Stream<E>. Because Collection is implemented by every collection, these methods can be used to obtain a stream from any collection class, such as ArrayList or HashSet. For example:


The second statement obtains a stream that uses arylist as a source.

Obtaining a Stream Through Arrays

A stream can also be obtained from an array by use of the several static overloads of the stream() method, which was added to the Arrays class. For example:


This statement obtains a stream that uses Integer array as a source. Streams can be obtained in a variety of other ways. However a stream is obtained, it can be used in the same way as any other stream.

Stream Example Program


StreamProgram Explanation

The program creates an ArrayList called arylist that holds a collection of integers. Next, it obtains a stream that uses arylist as a source through the use of the Next, the contents of the stream are displayed by use of forEach() method. The forEach() method executes an operation on each element in the stream. In this case, it simply calls System.out.print() for each element in the stream. The forEach() method has a parameter of type ConsumerConsumer is a generic functional interface declared in java.util.function. Its abstract method is void accept( ). The lambda expression in the call to forEach() provides the implementation of accept():


The forEach() method is a terminal operation. Thus, after it completes, the stream has been disconnects the source. Next, a stream is filtered by filter() so that it contains only less then 100 values. The filter() method filters a stream based on a predicate. It returns a new stream that contains only those elements that satisfy the predicate. Predicate is a generic functional interface defined in java.util.function. Its abstract method is boolean test(). It returns true if the object referred to by t satisfies the predicate, and false otherwise. The lambda expression passed to filter() implements this method:


Because filter() is an intermediate operation, it returns a new stream that contains filtered values, which, in this case, values are less then 100. These elements are then displayed via forEach() as before. Because filter(), or any other intermediate operation, returns a new stream, it is possible to filter a filtered stream a second time. This is demonstrated by the following line, which produces a stream that contains values between 50 and 100 :


Program Source

import java.util.ArrayList;

public class Javaapp {

    public static void main(String[] args) {

        ArrayList<Integer> arylist = new ArrayList<Integer>();

        Stream<Integer> strm =;
        System.out.println("All Elements");
        Stream<Integer> fstrm => e<100);
        System.out.println("Filtered Elements");

Leave a Comment