Java Stream API

Last modified on June 23rd, 2014 by Joe.

This Java tutorial is to give an introduction to the Stream API which is one of the core features of Java 8.

Pipelines and streams are introduced in Java SE 8 and it enriches the Java collections API. Java Stream API package is to support functional style operations such as map-reduce transformations on collections. At the heart of this package is “Stream”. Understanding Lambda expressions is a prerequisite to go through this Java Stream API tutorial.


Stream is a sequence of elements supporting sequential and parallel aggregate operations.

Stream is not a data type or a collection by itself. Sequence of elements are created out of a source, like a collection object, and transformed using different operations connected as pipelines and ends with a final operation.


Source – is an entity that stores or generates data. For example, it can be a collection or a random number generator.

Operations – Intermediate and terminal are two types of operations in a stream. Intermediate operation returns a new stream and terminal operation consumes a stream.

Stream – So the stream starts from a source and followed by one or more intermediate operations and ends with a terminal operation.

Example Stream: following code prints strings greater than length 5 by iterating a collection.

Collection fruitList = Arrays.asList("Apple", "Orange","Strawberry"); -> element.length() > 5)
	.forEach(element -> System.out.println(element));

Java Streams Operations

As stated above, Java stream operation are of two types intermediate and terminal. The intermediate operations are classified as stateful or stateless based on their ability to process elements independently.

If each of the element can be processed independently without retaining any information for processing other elements, then they are stateless. If any information is retained for processing of other elements, then they are stateful.


Stateless Intermediate Operations

For example,

are stateless intermediate operations. Every individual element can be processed without the need for sharing information between them for processing.

Stateful Intermediate Operations

For example,

etc are known as stateful operations. Because elements cannot be processed individually, they need to be compared with one another thus requiring information to be retained.

Terminal Operations

Terminal operations consumes the stream pipeline. It does not produce another stream like intermediate operations. It produces a result or a side-effect.

Following are some of the available terminal operations in the API,

Once the terminal operation completes execution, then the stream is completed and it cannot be used further. If there is a need to use similar stream, then a new stream should be created. This is similar to the concept of iteration using Iterable interface.

Eager and Lazy

Terminal operations are eager and intermediate steam operations are lazy. The intermediate operations just remain as a pipeline, and the elements are parse only when the terminal operation is executed. It is better since when there is a short-circuit the whole list of elements need not be processed, thus improving the performance.


Execution of a code block in terminal operation is a side-effect. This should be used with care as it can affect the stateful nature of a intermediate operation. Operations like forEach() and peek() can only be executed using a side-effect. It can be useful for simple System.out.println for debugging small Java programs. The code block at the start of this tutorial is an example for side-effect.


Both intermediate and terminal stream operations can do short-circuiting. If the source is an infinite one (for example, unlimited supply of random numbers), cutting it short to a finite list of elements by an intermediate operation is short-circuiting. When a terminal operation completes in final time even if the input from a stream is infinite, then it is short-circuiting. Short-circuiting is a necessary but not sufficient condition for an infinite source in a Java stream.


Order of the elements processed in an operation depends on the source in a Java stream. For example, in a sequential stream for java.util.List the order will be maintained. But in the case of Map it need not be maintained.

Java Stream Code Example

Following Java code example demonstrate the basic uses of streams.

package com.javapapers.java8;

import java.util.List;

public class PipelinesStreams {
	public static void main(String... args) {

		Animal animal = new Animal();
		List animalList = animal.getAnimalList();

		System.out.println("**** using regular for loop:");
		for (Animal a : animalList) {
			System.out.println(a.getName() + " " + a.getType());

		System.out.println("**** iteration using stream forEach:");
				a -> System.out.println(a.getName() + " " + a.getType()));

		System.out.println("**** stream with filter:");
				.filter(a -> a.getType() == Animal.WILD)
						a -> System.out.println(a.getName() + " " + a.getType()));

		System.out.println("**** stream with filter and count:");
		long count =
				.filter(a -> a.getType() == Animal.DOMESTIC).count();
				.println("Count of " + Animal.DOMESTIC + " animals: " + count);

				.println("**** stream with filters and average on a property:");
		double averageAge =
				.filter(a -> a.getType() == Animal.WILD)
				.filter(a -> a.getAge() > 2).mapToInt(Animal::getAge).average()
		System.out.println("Average age of " + Animal.WILD + " animals: "
				+ averageAge);

package com.javapapers.java8;

import java.util.ArrayList;
import java.util.List;

public class Animal {

	public final static String WILD = "Wild";
	public final static String DOMESTIC = "Domestic";

	private String name;
	private String type;
	private int age;

	public int getAge() {
		return age;

	public void setAge(int age) {
		this.age = age;

	public Animal() {

	public Animal(String name, String type, int age) { = name;
		this.type = type;
		this.age = age;

	public String getName() {
		return name;

	public void setName(String name) { = name;

	public String getType() {
		return type;

	public void setType(String type) {
		this.type = type;

	public List getAnimalList() {
		List animalList = new ArrayList();
		animalList.add(new Animal("Crocodile", WILD, 30));
		animalList.add(new Animal("Jaguar", WILD, 9));
		animalList.add(new Animal("Puma", WILD, 2));
		animalList.add(new Animal("Dog", DOMESTIC, 3));
		return animalList;

In the next Java tutorial, we will go to the next level in Java streams API.

Comments on "Java Stream API"

  1. Harita says:

    Very useful info.

    Stateful Intermediate Operations, Stateless Intermediate Operations headings are interchanged. Pls change them.

  2. Joe says:

    Hey thanks Harita. I have fixed it.

  3. khan says:

    Thanks Joe, Good one, waiting for lambda expression post

  4. Pratik says:

    Java 8 Rocks, thanks Joe for this nice tutorial

  5. Pratik says:

    as I remember ‘Lamda Expresison’ has already been covered by Joe

  6. Ranjeet Kharade says:

    Thanks Joe, Good one……..!!!

  7. Joe says:

    I have already written tutorials about lambdas in Java. Have a look at

  8. Joe says:

    Welcome Ranjeet.

  9. […] let us continue with the Java Stream API. Couple of weeks earlier we saw about the introduction to Java Stream API. There were some leftovers in that topic and lets have a look at them now. A warning for you, this […]

  10. […] best way to filter a Java collection is to use Java 8. Java streams and lambdas can be used to filter a collection as […]

  11. Ed says:

    Like your tutorials. This one has peek() listed as Stateful in the diagram but Stateless in the text. Which is it?

Comments are closed for "Java Stream API".