Streams

Java 8 Stream API

By Lokesh Gupta | Filed Under: Java 8

A Stream in Java can be defined as a sequence of elements from a source that supports aggregate operations on them. The source here refers to a Collections or Arrays who provides data to a Stream.

Stream keeps the ordering of the data as it is in the source. The aggregate operations or bulk operations are operations which allow us to express common manipulations on stream elements easily and clearly.

Table of Contents

1. Streams vs. Collections
2. Different ways to create streams
3. Converting streams to collections
4. Core stream operations
    4.1. Intermediate operations
    4.2. Terminal operations
5. Short-circuit operations
6. Parallelism

Before going ahead, it is important to learn that Java 8 Streams are designed in such a way that most of the stream operations returns streams only. This help us creating chain of the stream operations. This is called as pipe-lining. I will use this term multiple times in this post, so keep it in mind.

1. Java Stream vs. Collection

All of us have watch online videos on youtube or some other such website. When you start watching video, a small portion of file is first loaded into your computer and start playing. You don’t need to download complete video before start playing it. This is called streaming. I will try to relate this concept with respect to collections and differentiate with Streams.

At the basic level, the difference between Collections and Streams has to do with when things are computed. A Collection is an in-memory data structure, which holds all the values that the data structure currently has—every element in the Collection has to be computed before it can be added to the Collection. A Stream is a conceptually fixed data structure, in which elements are computed on demand. This gives rise to significant programming benefits. The idea is that a user will extract only the values they require from a Stream, and these elements are only produced—invisibly to the user—as and when required. This is a form of a producer-consumer relationship.

In java, java.util.Stream represents a stream on which one or more operations can be performed. Stream operations are either intermediate or terminal. While terminal operations return a result of a certain type, intermediate operations return the stream itself so you can chain multiple method calls in a row. Streams are created on a source, e.g. a java.util.Collection like lists or sets (maps are not supported). Stream operations can either be executed sequential or parallel.

Based on above points, if we list down the various characteristics of Stream, they will be as follows:

  • Not a data structure

  • Designed for lambdas

  • Do not support indexed access

  • Can easily be outputted as arrays or lists

  • Lazy access supported

  • Parallelizable

2. Different ways to create streams

Below is the most popular different ways to build streams from collections.

package com.gs.iilp.corejava.java8;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class Stream1 {
	public static void main(String[] args) {

		// 1) Empty stream
		System.out.println("1) Empty stream");
		Stream<String> emptyStream = Stream.empty();

		emptyStream.forEach(s -> {
			System.out.println(s);
		});

		// 2) Stream.of(val1, val2, val3….)
		System.out.println("2) Stream.of(val1, val2, val3….)");
		Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8);

		stream.forEach(i -> {
			System.out.println(i);
		});

		// 3) Stream.of(arrayOfElements) way 1
		System.out.println("3) Stream.of(arrayOfElements) way 1");
		Stream<Double> doubleStream = Stream.of(new Double[] { 3.23, 6.56, 676.90 });

		doubleStream.forEach(i -> {
			System.out.println(i);
		});

		// 4) Stream.of(arrayOfElements) way 2
		System.out.println("4) Stream.of(arrayOfElements) way 2");
		Long[] numbers = { 435435435l, 543543l, 435345435l, 435435435l, 35435435l };
		Stream<Long> numberStream = Arrays.stream(numbers);

		numberStream.forEach(i -> {
			System.out.println(i);
		});

		// 5) Stream of Collection
		System.out.println("5) Stream of Collection ");
		List<Integer> list = new ArrayList<Integer>();

		for (int i = 1; i < 10; i++) {
			list.add(i);
		}

		Stream<Integer> listStream = list.stream();
		listStream.forEach(p -> System.out.println(p));

		// 6) Stream.builder()
		/*
		 * When builder is used the desired type should be additionally specified in the
		 * right part of the statement, otherwise the build() method will create an
		 * instance of the Stream<Object>:
		 */
		// Stream<String> namesStream =
		// Stream.builder().add("Mohit").add("Rohit").add("Sumit").build();
		System.out.println("6) Stream.builder() ");

		Stream<String> namesStream = Stream.<String>builder().add("Mohit").add("Rohit").add("Sumit").build();
		namesStream.forEach(s -> System.out.println(s));

		// 7) Stream.generate()
		System.out.println("7) Stream.generate()");
		// Stream<Integer> numStreamThroughGenerate = Stream.generate(() -> 2); will
		// give infinite numbers
		Stream<Integer> numStreamThroughGenerate = Stream.generate(() -> 2).limit(4);
		numStreamThroughGenerate.forEach(s -> System.out.println(s));

		// 8) Stream.iterate()
		System.out.println("8) Stream.generate()");
		Stream<Long> numStreamThroughIterate = Stream.iterate(4l, n -> n + 2l).limit(5);
		numStreamThroughIterate.forEach(s -> System.out.println(s));

		// 9) Stream.iterate() exclusive range
		System.out.println("9) Stream.iterate() exclusive range");
		IntStream primitiveIntStream = IntStream.range(3, 6);
		primitiveIntStream.forEach(p -> System.out.println(p));

		// 10) Stream.iterate() inclusive range
		System.out.println("10) Stream.iterate() inclusive range");
		IntStream primitiveIntStreamInc = IntStream.range(3, 6);
		primitiveIntStreamInc.forEach(p -> System.out.println(p));

	}

}

Stream, filter , map ,reduce , collectors

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

class Student {
	private int id;
	private String name;
	private int age;

	public Student(int id, String name, int age) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

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

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
	}

}

public class Stream1 {
	public static void main(String[] args) {

		List<Student> students = Arrays.asList(new Student[] { new Student(1, "Mohit", 25), new Student(3, "Rohit", 27),
				new Student(45, "Hohit", 15) });

		// Filter
		students.stream().filter((st) -> (st.getName().startsWith("M") && st.getAge() == 25))
				.forEach(System.out::println);

		// Map (Convert Student objects to String of names where age is divisible by 5)
		students.stream().filter(st -> st.getAge() % 5 == 0).map(Student::getName).forEach(System.out::println);

		// Collectors Receive in a collection like List<String>
		List<String> studentNamesList = students.stream().filter(st -> st.getAge() % 5 == 0).map(Student::getName)
				.collect(Collectors.toList());

		System.out.println(studentNamesList);

		// Reduce names string to a single list with # in between
		Optional<String> str = students.stream().filter(st -> st.getAge() % 5 == 0).map(Student::getName)
				.reduce((s1, s2) -> (s1 + "#" + s2));
		System.out.println(str.get());

		// Ex:2 reduce
		String[] randomStatements = { "This", "is", "amazing" };

		String finalName = Arrays.stream(randomStatements).reduce((s1, s2) -> (s1 + "," + s2)).get();
		System.out.println(finalName);

		// Ex3 :

		Integer totalAge = students.stream().map(Student::getAge).reduce((a, b) -> (a + b)).get();
		System.out.println(totalAge);
	}

}
Student [id=1, name=Mohit, age=25]
Mohit
Hohit
[Mohit, Hohit]
Mohit#Hohit
This,is,amazing
67

Readings:

Last updated