Java 8 Stream API

For processing the objects collections, Java 8 is introduced. A stream is nothing but the objects sequence that give support to the different methods that can be pipelined for producing the result that is required. Before proceeding with this topic further, it is advised to the readers to gain the basic knowledge of Java 8.

Creation of Streams

There are various ways to create the stream instance of the various resources.

Empty Stream

In order to create the empty stream, one must use the empty() method:

Syntax:

E: The different types of stream elements.

Return Value: An empty sequential of stream is returned.

Note: When invoking methods that has the stream parameters, an empty stream can be helpful in order to avoid the nullpointer exceptions.

Example

FileName: EmptyStream.java

Output:

Nothing to display

From Collections

A stream can be created of any type of Collection (Collection, List, Set):

Example

FileName: StreamCreationEx.java

Output:

J2EE
JAVA
Hibernate
Spring
J2EE
JAVA
Hibernate
Spring
JAVA
Hibernate
J2EE
Spring

Using Arrays

Array can also be the source of the Stream or Array can also be generated from the array that is existing or from the portion of the array:

FileName: StreamCreationEx.java

Output:

a1
b1
c1
d1
a1
b1
c1
d1
a1
b1
c1
d1

Using Stream.builder()

When a builder is utilized, the type that is desired must be specified additionally in the right side part of the statement, else, the method build() creates the Stream<Object> instance:

Syntax:

Parameters:

E: The different types of stream elements.

Return Value: A stream builder is returned.

Example

FileName: StreamCreationEx.java

Output:

a1
b1
c1

Using Stream.generate()

Stream generate(Supplier<T> s) returns an infinite sequential unordered stream where every element is produced by the Supplier provided. It is suitable for creating constant streams, streams of the random elements, etc.

Syntax:

Where, stream is an interface and E is the type of stream elements. sup is the Supplier of the elements generated and the value of return is the new infinite sequential unordered Stream.

Example

FileName: StreamCreationEx.java

Output:

-412391913
1531711136
-432916310
341021951
-615096017
1339859082

Using Stream.iterate()

The iterate(E, java.util.function.Predicate, java.util.function.UnaryOperator) method allows the iteration of the elements of stream until the mentioned condition. The method returns a sequential ordered Stream generated by the iterative application of the provided next function to the starting element, satisfying the condition hasNext predicate that is being passed as the parameter. The stream gets terminated as soon as the condition hasNext gives a false value.

Syntax:

Parameters: The method has a total of three parameters:

st: it is the starting element,

hasNext: it is the predicate that is applied to the elements for determining whether the stream should terminate or not

next: it is a function that is applied to the previous element in order to produce a new element.

Return value: The method will return a new sequential Stream.

Example

FileName: StreamCreationEx.java

Output:

2.0
1.0
0.5
0.25
0.125

Using Stream of Primitives()

Java 8 gives the facility for the creation of streams out of the three primitive types: int, long and double. As the Stream<E> is the generic interface, and there is no way for using the primitives as the type parameter with generics, a total of the three new special interfaces were made: IntStream, LongStream, DoubleStream.

IntStream Example

FileName: IntStream.java

Output:

Sum of the operation of intStreamRangeTest : 3
Time Elapsed of the intStreamRangeTest: 41313298
Sum of the operation of intStreamRangeClosedTest: 10
Time elapsed of the intStreamRangeClosedTest: 562842

LongStream Example

FileName: LongStream.java

Output:

The total count is: 3

DoubleStream Example

FileName: DoubleStream.java

Output:

The total count is: 3

Using Stream of String

Strings can also be used for creating stream by taking assistance of the chars() method, which is present in the String class.

Stream of String

FileName: StringStream.java

Output:

The total number of character count is: 10

Referencing A Stream

We can do the instantiation of the stream, and have a reference accessible to it, as long as only operations that intermediate are made. After executing a terminal operation, the stream becomes inaccessible. The illustration of it is mentioned below.

FileName: IllegalStateExpn.java

Output:

Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
	at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:647)
	at StreamCreationEx.main(StreamCreationEx.java:14)

Explanation: We have got the IllegalStateException in the main thread. It is because the streams in Java 8 can never be reused, and this behaviour is logical too. It is because the streams are designed to apply the finite sequence of operations to the elements source in the functional style, not to keep elements. If we want to avoid this exception, we should write the following code.

FileName: IllegalStateExpnCorrection.java

Output:

Optional[a1]

Stream Pipeline

In order to do operations sequentially, over the elements of data source and do the aggregation of their results, three parts are needed.

The source
The intermediate operation(s)
A terminal operation.

Intermediate operations modify or filter the elements in the stream, and a new stream is returned. Examples: filter, distinct, map, limit, sorted.

Terminal operations generate a side effect or result, marking the end of a stream. Examples: forEach, reduce, collect, min, count, anyMatch, max, noneMatch, allMatch.

Pipelines does chain of intermediate and terminal operations, processing data in a expressive and fluent manner. Each operation receives input from the previous operation and generates output for the next operation. Pipelines allow declarative and concise coding. From the terminal operation, the result is found.

FileName: PipelineStream.java

Output:

The sum of twice of odd numbers in the input list is: 78

Lazy Evaluation

Lazy evaluation also known as the call-by-need evaluation is the evaluation strategy, in which the evaluation of strategies is delayed unless its value is not required. All operations that are intermediate are executed on the stream if a terminal operation is called on it. In Java stream, lazy evaluation is one of the main features that permits the significant optimizations.

FileName: LazyEvaluation.java

Output:

Result is: 
Filter Done: 5
Filter Done: 9
Filter Done: 15
[5, 9, 15]

Explanation: It is general known fact that, the first statement inside the main method executed first, and then the second statement, and after that the third statement, and so on. Going by this concept, the output should be:

However, it is not the case. The print statement that contains "Result is: " gets executed first, and the filter() and peek() methods are not executed at all. These methods get executed when the terminal operation collect() is executed in the last print statement.

Points to Remember About Java Streams

The following are some important points that a user must know.

  • Streams are sequence of elements that can be processed or modified using the pipeline of operations. They are not data structures.
  • Streams provide support to two operations types: one is intermediate and other is terminal. Intermediate operations filter or transform the stream, while terminal operations produce a side effect or result.
  • The way streams are designed, make them lazy, which means processing of elements happens when there is a demand of it.
  • Java Streams are not apt for every scenario. In some of the cases, traditional iteration with the help of loops may be more efficient and appropriate.