Java 8

Java8 – Streams

Java provides a new additional package in Java 8 called java.util.stream. This package consists of classes, interfaces and enum to allows functional-style operations on the elements. You can use stream by importing java.util.stream package.

What is Stream?

Stream represents a sequence of objects from a source, which supports aggregate operations. Following are the characteristics of a Stream −

  1. Sequence of elements − A stream provides a set of elements of specific type in a sequential manner. A stream gets/computes elements on demand. It never stores the elements.
  2. Source − Stream takes Collections, Arrays, or I/O resources as input source.
  3. Aggregate operations − Stream supports aggregate operations like filter, map, limit, reduce, find, match, and so on.
  4. Pipelining − Most of the stream operations return stream itself so that their result can be pipelined. These operations are called intermediate operations and their function is to take input, process them, and return output to the target. collect() method is a terminal operation which is normally present at the end of the pipelining operation to mark the end of the stream.
  5. Automatic iterations − Stream operations do the iterations internally over the source elements provided, in contrast to Collections where explicit iteration is required.

Stream Creation

There are many ways to create a stream instance of different sources. Once created, the instance will not modify its source, therefore allowing the creation of multiple instances from a single source.

  1. Empty Stream – We should use the empty() method in case of the creation of an empty stream. We often use the empty() method upon creation to avoid returning null for streams with no element
  2. Stream of Collection – We can also create a stream of any type of Collection (Collection, List, Set)
  3. Stream of Array – An array can also be the source of a stream
  4. Stream.generate() – The generate() method accepts a Supplier<T> for element generation. As the resulting stream is infinite, the developer should specify the desired size, or the generate() method will work until it reaches the memory limit
  5. Stream.iterate() – Another way of creating an infinite stream is by using the iterate() method
  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>
// 1. Create empty stream. 
Stream<String> emptyStream = Stream.empty();

// 2. Stream of Collection
Collection<String> collection = Arrays.asList("a", "b", "c");
Stream<String> streamOfCollection = collection.stream();

// 3. Stream of Array
String[] arr = new String[]{"a", "b", "c"};
Stream<String> streamOfArrayFull = Arrays.stream(arr);
Stream<String> streamOfArrayPart = Arrays.stream(arr, 1, 3);

// 4. Generate Stream
Stream<String> streamGenerated =
		  Stream.generate(() -> "element").limit(10);

// 5. Stream Iterate - The first element of the resulting stream is the first parameter
// of the iterate() method. When creating every following element, the specified function is applied 
// to the previous element. In the example above the second element will be 42.
Stream<Integer> streamIterated = Stream.iterate(40, n -> n + 2).limit(20);

// 6. Stream Builder
Stream<String> streamBuilder =
		  Stream.<String>builder().add("a").add("b").add("c").build();

Sequential Processing

stream() − Returns a sequential stream considering collection as its source.

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
strings.stream().filter(string -> !string.isEmpty())
.forEach(System.out::println);

Parallel Processing

parallelStream is the alternative of stream for parallel processing. Take a look at the following code segment that prints a count of empty strings using parallelStream.

List strings1 = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
strings1.parallelStream().filter(string -> !string.isEmpty())
.forEach(System.out::println);

It is very easy to switch between sequential and parallel streams.

forEach

Stream has provided a new method ‘forEach’ to iterate each element of the stream. The following code segment shows how to print 10 random numbers using forEach.

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

map

The ‘map’ method is used to map each element to its corresponding result. The following code segment prints unique squares of numbers using map.

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List<Integer> squaresList = numbers.stream().map( i -> i*i)
.distinct().collect(Collectors.toList());

filter

The ‘filter’ method is used to eliminate elements based on a criteria. The following code segment prints a count of empty strings using filter.

List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
//get count of empty string
long count = strings.stream().filter(string -> string.isEmpty()).count();

limit

The ‘limit’ method is used to reduce the size of the stream. The following code segment shows how to print 10 random numbers using limit.

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

sorted

The ‘sorted’ method is used to sort the stream. The following code segment shows how to print 10 random numbers in a sorted order.

Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);

Collectors

Collectors are used to combine the result of processing on the elements of a stream. Collectors can be used to return a list or a string.

List<String> filtered = strings.stream()
.filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("Filtered List: " + filtered);
		
String mergedString = strings.stream()
.filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("Merged String: " + mergedString);

Statistics

With Java 8, statistics collectors are introduced to calculate all statistics when stream processing is being done.

List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream()
.mapToInt((x) -> x).summaryStatistics();

System.out.println("Highest number in List : " + stats.getMax());
System.out.println("Lowest number in List : " + stats.getMin());
System.out.println("Sum of all numbers : " + stats.getSum());
System.out.println("Average of all numbers : " + stats.getAverage());

Stream Example

Create the following Java program using any editor of your choice in, say, C:\hitechpoints\workspace\java8\src>.

Java8Stream.java
import java.util.Arrays;
import java.util.Collection;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Java8Stream {
	
	public static void main(String[] args) {
		// 1. Create empty stream. 
		Stream<String> emptyStream = Stream.empty();
		System.out.println("Empty stream count = " + emptyStream.count());
		
		// 2. Stream of Collection
		Collection<String> collection = Arrays.asList("a", "b", "c");
		Stream<String> streamOfCollection = collection.stream();
		System.out.println("Stream of collection = " + streamOfCollection.count());
		
		// 3. Stream of Array
		String[] arr = new String[]{"a", "b", "c"};
		Stream<String> streamOfArrayFull = Arrays.stream(arr);
		System.out.println("Stream of array = " + streamOfArrayFull.count());
		Stream<String> streamOfArrayPart = Arrays.stream(arr, 1, 3);
		System.out.println("Stream of array part = " + streamOfArrayPart.count());
		
		// 4. Generate Stream
		Stream<String> streamGenerated =
				  Stream.generate(() -> "element").limit(10);
		System.out.println("Generate Stream count = " + streamGenerated.count());
		
		// 5. Stream Iterate - The first element of the resulting stream is the first parameter
		// of the iterate() method. When creating every following element, the specified function is applied 
		// to the previous element. In the example above the second element will be 42.
		Stream<Integer> streamIterated = Stream.iterate(40, n -> n + 2).limit(20);
		System.out.println("Stream Iterated count = " + streamIterated.count());
		
		// 6. Stream Builder
		Stream<String> streamBuilder =
				  Stream.<String>builder().add("a").add("b").add("c").build();
		System.out.println("stream Builder count = " + streamBuilder.count());
		
		// 7. Sequential Processing
		List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
        strings.stream().filter(string -> !string.isEmpty()).forEach(System.out::println);
		
        // 8. Parallel Processing
		strings.parallelStream().filter(string -> !string.isEmpty()).forEach(System.out::println);
		
		// 9. limit with forEach Method
		Random random = new Random();
		random.ints().limit(10).forEach(System.out::println);
		 
		// 10. Distinct values
		List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
		List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
		System.out.println("Distinct list = " + squaresList);
		
		// 11. Filter empty string 
		long count = strings.stream().filter(string -> string.isEmpty()).count();
		System.out.println("Filter emprty string count = " + count);
		
		// 12. Sort the values
		random.ints().limit(10).sorted().forEach(System.out::println);
		
		// 13. Return list using collectors
		List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
		System.out.println("Filtered List = " + filtered);
		
		// 14. Merge string using collectors joining
		String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
		System.out.println("Merged String = " + mergedString);
		
		// 15. calculate all statistics using aggregate method
		IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();

		System.out.println("Highest number in List = " + stats.getMax());
		System.out.println("Lowest number in List = " + stats.getMin());
		System.out.println("Sum of all numbers = " + stats.getSum());
		System.out.println("Average of all numbers = " + stats.getAverage());
	}

}

Output:

Compile the class using javac compiler as follows −

C:\hitechpoints\workspace\java8\src>javac Java8Stream.java

Now run the Java8Stream as follows −

C:\hitechpoints\workspace\java8\src>java Java8Stream

It should produce the following result −

Empty stream count = 0
Stream of collection = 3
Stream of array = 3
Stream of array part = 2
Generate Stream count = 10
Stream Iterated count = 20
stream Builder count = 3
abc
bc
efg
abcd
jkl
abcd
efg
jkl
abc
bc
244461881
-1828441254
-709997954
6330226
-683154309
1461940392
1414932
-41689475
-527991849
-1798635761
Distinct list = [9, 4, 49, 25]
Filter emprty string count = 2
-2016346550
-1730603053
-1614390501
-934536364
-602554008
-327371913
-321432833
676728532
944873512
1837153914
Filtered List = [abc, bc, efg, abcd, jkl]
Merged String = abc, bc, efg, abcd, jkl
Highest number in List = 7
Lowest number in List = 2
Sum of all numbers = 25
Average of all numbers = 3.5714285714285716

About the Author: Elavarasan PK

Technical Specialist, Intersoft Data Labs