Java 9 – Stream API Enhancements | Code Factory


Index Page : Link

Donate : Link

Medium Blog : Link

Applications : Link

Streams concept has been introduced in Java 1.8 version.

The main objective of Stream concept is to process elements of Collection with Functional Programming (Lambda Expressions).

What is the difference between java.util streams and java.io streams?

java.util streams meant for processing objects from the collection. ie. it represents a stream of objects from the collection but java.io streams meant for processing binary and character data with respect to file. i.e it represents stream of binary data or character data from the file. Hence java.io streams and java.util streams are different concepts.

What is the difference between Collections and Streams?

If we want to represent a group of individual objects as a single entity, then we should go for collection. If we want to process a group of objects from the collection then we should go for Streams.

How to Create Stream Object?

We can create stream object to the collection by using stream() method of Collection interface. stream() method is a default method added to the java.util.Collection class in 1.8 version.

default Stream<E> stream()

Ex: Stream s = c.stream(); // c is any Collection object

Note: Stream is an interface present in java.util.stream package.

How to process Objects of Collection By using Stream:

Once we got the stream, by using that we can process objects of that collection. For this we can use either filter() method or map() method.

Processing Objects by using filter() method:

We can use filter() method to filter elements from the collection based on some boolean condition.

Stream<T> filter(Predicate<? super T> predicate);

Processing Objects by using map() method:

If we want to create a separate new object, for every object present in the collection based on our requirement then we should go for map() method of Stream interface.

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

Processing Objects by using flatMap() method:

Both map and flatMap can be applied to a Stream and they both return a Stream. The difference is that the map operation produces one output value for each input value, whereas the flatMap operation produces an arbitrary number (zero or more) values for each input value.

Typical use is for the mapper function of flatMap to return Stream.empty() if it wants to send zero values, or something like Stream.of(x, y, z) if it wants to return several values.

package com.codeFactory;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CodeFactory {
	public static void main(String... args) throws Exception {
		List<Integer> l1 = List.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
		System.out.println("Before using map() method: " + l1);
		
		List<Integer> l2 = l1.stream().map(l -> l*2).collect(Collectors.toList());
		System.out.println("After using map() method: " + l2);
		
		List<Integer> l3 = l1.stream().flatMap(l -> Stream.of(l)).collect(Collectors.toList());
		System.out.println("After using flatMap() method: " + l3);
		
		List<Integer> l4 = l1.stream().flatMap(l -> Stream.of(l, l*l)).collect(Collectors.toList());
		System.out.println("After using flatMap() method: " + l4);
	}
}

Output:

Before using map() method: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
After using map() method: [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
After using flatMap() method: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
After using flatMap() method: [0, 0, 1, 1, 2, 4, 3, 9, 4, 16, 5, 25, 6, 36, 7, 49, 8, 64, 9, 81, 10, 100]

map: <R> Stream<R> map(Function<? super T, ? extends R> mapper);

flapMap: <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

Java 9 Enhancements for Stream API:

In Java 9 as the part of Stream API, the following new methods introduced.

  1. takeWhile()
  2. dropWhile()
  3. Stream.iterate()
  4. Stream.ofNullable()

Note: takeWhile() and dropWhile() methods are default methods and iterate() and ofNullable() are static methods of Stream interface.

1. takeWhile():

default Stream<T> takeWhile(Predicate<? super T> predicate)

It returns the stream of elements that matches the given predicate. It is similar to filter() method.

Difference between takeWhile() and filter():

filter() method will process every element present in the stream and consider the element if predicate is true.

But, in the case of takeWhile() method, there is no guarantee that it will process every element of the Stream. It will take elements from the Stream as long as predicate returns true. If predicate returns false, at that point onwards remaining elements won’t be processed, i.e rest of the Stream is discarded.

Eg: Take elements until we will get even numbers. Once we got odd number then stop and ignore rest of the stream.

package com.codeFactory;

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

public class CodeFactory {
	public static void main(String... args) throws Exception {
		List<Integer> l1 = List.of(2, 4, 5, 6, 7, 8, 9, 10);
		System.out.println("List: " + l1);
		
		List<Integer> l2 = l1.stream().filter(i -> i%2==0).collect(Collectors.toList());
		System.out.println("After filter() method: " + l2);
		
		List<Integer> l3 = l1.stream().takeWhile(i -> i%2==0).collect(Collectors.toList());
		System.out.println("After takeWhile() method: " + l3);
	}
}

Output:

List: [2, 4, 5, 6, 7, 8, 9, 10]
After filter() method: [2, 4, 6, 8, 10]
After takeWhile() method: [2, 4]

2. dropWhile()

default Stream<T> dropWhile(Predicate<? super T> predicate)

It is the opposite of takeWhile() method.
It drops elements instead of taking them as long as predicate returns true. Once predicate returns
false then rest of the Stream will be returned.

package com.codeFactory;

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

public class CodeFactory {
	public static void main(String... args) throws Exception {
		List<Integer> l1 = List.of(2, 4, 5, 6, 7, 8, 9, 10);
		System.out.println("List: " + l1);
		
		List<Integer> l2 = l1.stream().filter(i -> i%2==0).collect(Collectors.toList());
		System.out.println("After filter() method: " + l2);
		
		List<Integer> l3 = l1.stream().dropWhile(i -> i%2==0).collect(Collectors.toList());
		System.out.println("After takeWhile() method: " + l3);
	}
}

Output:

List: [2, 4, 5, 6, 7, 8, 9, 10]
After filter() method: [2, 4, 6, 8, 10]
After takeWhile() method: [5, 6, 7, 8, 9, 10]

3. Stream.iterate():

Form-1: iterate() method with 2 Arguments

This method introduced in Java 8.

public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)

It takes an initial value and a function that provides next value

Stream.iterate(1, i -> i+1).forEach(System.out::println);

Output:

1
2
3
... infinite times

How to limit the number of iterations:

Stream.iterate(1, i -> i+1).limit(5).forEach(System.out::println);

Output:

1
2
3
4
5

Form-2: iterate() method with 3 arguments

The problem with 2 argument iterate() method is there may be a chance of infinite loop. To avoid, we should use limit method.

To prevent infinite loops, in Java 9, another version of iterate() method introduced, which is nothing but 3-arg iterate() method.

This method is something like for loop

for(int i =0;i<10;i++){}

public static<T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)

Stream.iterate(1, i -> i<=5, i -> i+1).forEach(System.out::println);

Output:

1
2
3
4
5

4. ofNullable():

public static<T> Stream<T> ofNullable(T t)

This method will check whether the provided element is null or not. If it is not null, then this method returns the Stream of that element. If it is null then this method returns empty stream.

This method is helpful to deal with null values in the stream
The main advantage of this method is to we can avoid NullPointerException and null checks everywhere.

Usually we can use this method in flatMap() to handle null values.

package com.codeFactory;

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

public class CodeFactory {
	public static void main(String... args) throws Exception {
		List<Integer> l1 = Arrays.asList(2, 4, 5, null, 6, null, 7);
		System.out.println("List: " + l1);
		
		List<Integer> l2 = l1.stream().filter(i -> i!=null).collect(Collectors.toList());
		System.out.println("After filter() method: " + l2);
		
		List<Integer> l3 = l1.stream().flatMap(i -> Stream.ofNullable(i)).collect(Collectors.toList());
		System.out.println("After takeWhile() method: " + l3);
	}
}

Output:

List: [2, 4, 5, null, 6, null, 7]
After filter() method: [2, 4, 5, 6, 7]
After takeWhile() method: [2, 4, 5, 6, 7]

Leave a comment