Introduction to Java Stream API

Knoldus Blog Audio
Reading Time: 3 minutes

Hi friends in this blog we are going to cover streams, parallel streams and some of the popular function associated with them with example.

Introduction

Streams are wrappers around data source, which make data processing in bulk easier and fast. A stream is not a data structure i.e. it does not store data. Stream is defined in java.util.stream package.

Different ways to create Streams

1) Using Steam.of()
private static Player[] players = {
    new Player(1, "Sachine", 42000), 
    new Player(2, "Virat", 20000), 
    new Player(3, "Dhoni", 18000),
    new Player(4, "Gambhir", 21000)
};
Stream.of(players);
2) Using Stream.builder()
Stream.Builder<Player> playerStreamBuilder = Stream.builder();

playerStreamBuilder.accept(players[0]);
playerStreamBuilder.accept(players[1]);
playerStreamBuilder.accept(players[2]);

Stream<Player> playerStream = playerStreamBuilder.build();
3) Using existing collection
List<Player> playerList = Array.asList(players);
playerList.stream();

Stream Operations

Now we are going to see some common usages and how to perform some of the popular operations. There are many method defined to make it easy to use streams and makes developers life easy.

1) forEach

It is a terminal operation which loops over the stream elements, here terminal operation means that once this method is called then the stream pipeline gets consumed and we cannot use it again. Below example will display name of all the players in the playerList.

playerList.stream().forEach( e -> System.out.println(e.getName()));
2) map

Map is an intermediate operation which return new stream after applying a function and these operations are always lazy because of this, its get executed at the time of requirement not at the time of initialisation.

List<Integer> salaries = Arrays.asList(10000, 20000, 30000);

// Increment salary by 10%

salaries.stream().map(salary -> salary+salary*10/100).forEach(System.out::println); 
3) flatMap

Flatmap is an intermediate operation which first flattens the stream of collection to a stream of object and after that it perform mapping of objects. We generally performs this operation to complex collection like: List<List<Integer>>

List<List<Integer>> listOfNumbers = Arrays.asList(Arrays.asList(1,2),  Arrays.asList(3,4));

List<Integer> listOfIntegers =  listOfNumbers.strem().flatMap(x -> x.stream()).collect(Collectors.toList));
4) collect

Collect is terminal operation, which repackage elements to some data structure and apply some logic operation.

5) filter

This returns a new streams after applying some tests on the original stream, this method is an intermediate operation we can also use it multiple times so that the readability of the code can be increased.

// this list will contains player with more than 20000 runs
List<Player> filteredPlayers =  playerList.stream()
			.filter(c -> c.getRun() > 20000)
			.collect(Collectors.toList());
6) reduce

Reduce is a terminal operation and mainly used when we want to reduce streams to single value, for example maximum, minimum, sum etc.

// this exmple demonstrate the use of reduce operation

List<String> list = Arrays.asList(“Welcome”, “to”, “stream”, “apis”);

Optional<String> smallestString = list.stream()
				.reduce((w1, w2)-> w1.length() < w2.length()
					? w1:w2);
smallestString.ifPresent(System.out::println);
7) sorted

This is an intermediate operation, which sort elements present within the streams. Use of sorted is very common specially on the collection of streams, because it makes code concise and hence increases readability. Now we will look at two example.

/ this sorts elements in natural order(assending order)
Arrays.asList(14, 7, -5, 18, -89).stream.sorted().forEach(System.out::println);

// this sorts elements in dessending order
Arrays.asList(14, 7, -5, 18, -89) .stream.sorted(Collections.reverseOrder()).forEach(System.out::println);

Parallel Streams

The stream apis enables developer to take advantages of multi core system architecture by using the stream api to enhance performance of the applications because in parallel streams, streams are processed parallel by multi-core processors, there are two ways to create parallel streams

1) By using parallelStreams()
Stream parallelStreams = Arrays.asList(“Mercedes”, “Audi”, “Land Rover”, “Vokswagon”).stream().parallel();
2) By using parallel()
Stream parallelStreams = Arrays.asList(“Mercedes”, “Audi”, “Land Rover”, “Vokswagon”).parallelStream();
When to use parallel streams
  • we should use parallel streams when the output of the operation is not dependent on the order of elements present in source collection and because of this we can get output quickly. But using it on dependent operations can make it more worse.
  • parallel streams can be used in case of aggregate functions because operations on aggregate functions can be performed independently.
Below diagram demonstrate the working of sequential and parallel streams

Reference

For more information on Streams and related methods, Click here.