Java 8 Stream API Tutorial

Java 8 Stream API Tutorial


This tutorial will provide you an overview of Java 8 Stream API along with example code snippets. Stream is a new API introduced in Java 8 which supports various operations to efficiently process data. Let’s start with the definition of Stream.

Advertisements

What is Stream?

If you check the API doc of Java 8, Stream is nothing but a typed interface. It represents a sequence of objects which supports sequential and parallel aggregate operations. A thing to note that Stream does not hold any data, it only processes the data as per operations performed on it. It does not change the data under process.

Do not confuse Stream with any Collection API class. Collection API classes like ArrayList, HashMap holds data but Stream does not hold any data. You can create Stream from these collection classes but Stream will not replace these classes.

How to create Stream?

A stream can be obtained from collection classes, arrays, Stream.Builder etc. Let’s check all of them one by one:

Stream of Collection

The java.util.Collection  interface has default method stream()  which provides a stream of items in the collection. This also gets inherited to all its sub-interfaces like List, Set.

Collection<String> stringCollection = Arrays.asList("One", "Two", "Three");
Stream<String> streamOfCollection = stringCollection.stream();

 Stream of values

The Stream  interface has a static method of(T… values)  which returns stream of all elements given in the argument.

Stream<Integer> streamOfIntegers = Stream.of(1, 2, 3);

Stream of Array

The java.util.Arrays  class provides methods to create a stream from an array. The source can be an array of objects or array of primitives. In case of primitives, separate stream interfaces specific to that primitive type is also available. For example, IntStream, LongStream, and DoubleStream.

Person[] personArray = {new Person("bytes", 18), new Person("tree", 20)};
Stream<Person> personStream = Arrays.stream(personArray);

int[] intArray = {1, 2, 3};
IntStream intStream = Arrays.stream(intArray);

Stream from Stream.Builder

In case you want to add an individual element in the stream, Stream provides a builder method. This will return an instance of Stream.Builder which allows adding each element individually.

Stream.Builder<String> builder = Stream.builder();
Stream<String> streamFromBuilder = builder.add("One").add("Two").add("Three").build();

Infinite Stream

We can create an infinite stream by keep on applying a particular operation/function to the result of previous operation/function. For example, if you want a stream containing elements such that each element is greater than 2 from the previous element. If we start with 0 as the first element then stream should be 0, 2, 4, 6, 8 and so on.

Stream provides Stream<T> iterate(T seed, UnaryOperator<T> f)  method for creating such stream. But remember that you have to limit on how many such elements you want in the stream, otherwise it will eat up the memory.

Stream<Integer> streamFromIterate = Stream.iterate(0, n -> n + 2).limit(10);

Operations in Stream

After getting a stream from its source, we can start performing various operations on it. Stream operations are broadly categorized into two types: Intermediate Operations and Terminal Operations.

Java 8 Stream API Tutorial

Intermediate Operations

These operations are also known as “lazy” operations. They are “lazy” because the actual processing will not happen unless the terminal operation is present. The easiest way to identify intermediate operations is to check the return type of the operation. These operations return a stream after completing the process. For example, map() and filter() methods in Stream.

Terminal Operations

As name suggests, these are final operations performed on a Stream. Terminal operations do not have return type as Stream like intermediate operations. They either return a value or nothing at all. For example, count() and forEach() methods.

A thing to note here that intermediate operations will execute only if a terminal operation is present in the chain of operations.

For example, In below code only intermediate operations are performed on a stream, as a result, nothing is printed.

Stream.of("one", "two", "Three").map(s -> s.toUpperCase())
		.peek(System.out::println);

If you add a count() (or any terminal operation like collect) at the end, it will print elements of string in upper case by executing preceding intermediate operations.

Stream.of("one", "two", "Three").map(s -> s.toUpperCase())
	.peek(System.out::println).count();

Remember that order of operations on stream should be:

  1. Source of Stream
  2. Zero/more intermediate operations
  3. Terminal operation
Advertisements

Stream Examples

Enough with theory now. Let’s see some practical use of Stream API.

Convert List of objects to another List of objects

Below example creates List of String from List of Person object by using name property of Person.

List<Person> persons = new ArrayList<>();
persons.add(new Person("Bytes", 18));
persons.add(new Person("Tree", 20));
persons.add(new Person("Streams", 22));

List<String> names = persons.stream().map(p -> p.getName()).collect(Collectors.toList());
System.out.println(names);

Output:

[Bytes, Tree, Streams]

Create new list by filtering elements in List

Below example filters even numbers from List of integers and put them in a new List.

//create list of integers from 1 to 10
List<Integer> numbers = Stream.iterate(1, n -> n + 1).limit(10).collect(Collectors.toList());
List<Integer> evenNumbers = numbers.stream().filter(n -> (n.intValue() % 2 == 0))
		.collect(Collectors.toList());
System.out.println(evenNumbers);

Output:

[2, 4, 6, 8, 10]

Find element from a List by property of element

List<Person> persons = new ArrayList<>();
persons.add(new Person("Bytes", 18));
persons.add(new Person("Tree", 20));
persons.add(new Person("Streams", 22));

Optional<Person> result = persons.stream().filter(p -> p.getName().equals("Bytes")).findAny();
if (result.isPresent()) {
	System.out.println(result.get());
} else {
	System.out.println("Not Found");
}

Output:

Person:: Name: Bytes, Age: 18

Find maximum and minimum number from List of Integers

List<Integer> integers = Arrays.asList(3, 5, 45, 52, 5, 14);

Optional<Integer> max = integers.stream().max(Comparator.naturalOrder());
Optional<Integer> min = integers.stream().min(Comparator.naturalOrder());

System.out.println("Max: " + max.get());
System.out.println("Min: " + min.get());

Output:

Max: 52
Min: 3

Get sum of integers in using reduce operation

int[] intArray = {23,45,76,12,67,89};
Arrays.stream(intArray).reduce(Integer::sum)
		.ifPresent(s -> System.out.println("Sum: " + s));

Output:

Sum: 312
Advertisements

Source Code

Complete source code of examples given in this tutorial is available on Github

Hope this helps you to understand the basic of Stream API. For any queries, suggestions please leave your message in comments section.