Java 8 Stream Tutorial
One of the big innovations of Java 8 is the new Streams API (Package java.util.stream). A stream in the sense of this API is simply a sequence of elements. Such a stream can consist of a collection’s elements, but the elements can also be read from another source into the stream.
In Java 8, the interface java.util.stream.Stream<T> introduced powerful options for performing operations on arrays and lists.
Streams of the interface java.util.stream.Stream<T>, not to be confused with the input and output streams of the package java.io, represent streams of references that allow chained operations to be carried out on these references one after the other or in parallel. The data represented by the references are not modified by the stream itself.
The API offers a lot of operations to process elements that previously required processing with loops.
[st_adsense]
Motivation for using streams
To iterate over the elements of a list a ‘foreach’ notation is mostly used. Most programs contain many code digits like the following:
import java.util.*; public class Main { public static void main(String []args) { List<String> list = Arrays.asList("Alex", "Emily", "Jean"); for (String item:list) { System.out.println(item.length()); } } }
Output:
4 5 4
Each time each element of a list is to be processed, a loop must be written. The Streams API offers various ways to process the elements and apply functions to them. The stream equivalent to a loop is the forEach() method.
import java.util.*; import java.util.function.*; public class Main { public static void main(String []args) { List<String> list = Arrays.asList("Alex", "Emily", "Jean"); list.stream().forEach(new Consumer<String>() { @Override public void accept(String item) { System.out.println(item.length()); } }); } }
Output:
4 5 4
The stream() method on java.util.Collection interface creates a stream from the list. The stream method forEach() accepts an object of the type Consumer that has only one method, namely accept(String item). This method is called for each element in the stream. The output is the same for both versions.
[st_adsense]
Streams and lambda expressions
The version with the stream has hardly any advantage in the above form: the code seems longer and more complex than the simple solution with the loop. Thankfully, Java 8 offers a new solution for this code: Lambda expressions(or functional expressions).
The accept() method accepts an object of a certain type and has no return value. With such a lambda expression the code can be simplified as follows:
list.stream().forEach((String item) -> System.out.println(item.length()));
Since the Java compiler can derive the type of the function from the generic type of the list (List<String>), the expression can be further simplified:
list.stream().forEach(item -> System.out.println(item.length()));
Since the method System.out.println() has the same signature as the accept() method, a reference to the method can also be passed:
list.stream().forEach(System.out::println);
This version is now really shorter and also with Lambda Expressions is easier to read.
Method-Chaining with Streams
Some methods of the stream interface return a stream themselves. So it is possible to filter or modify elements in streams:
List<Integer> lengthList = list.stream() .filter(item -> item.length() > 4) .map(String::length) .collect(Collectors.toList());
In this example, all elements longer than 4 are filtered out first. Then the lengths are calculated. The method map() converts the string into a number (the length of the string) with the function String::length and returns a stream of type integer. The method collect() converts the values of the integer stream back into a list. The content of the list now looks like this:
[5][st_adsense]