Java Stream API is there since Java 8. It is used to express computation on data is a short and elegant way. In the following post I will introduce you to the most common methods to give you an idea what you can achieve with the Java Stream API.
Before jumping right into the code I want to explain the difference between Streams and Collections. It is clear that both have properties in common, they can both be iterated for example. You can iterate through Collection external with e.g. for each loops. Instead you don’t loop explicit through streams. You express your computations in a functional way and the Stream API deals for iterations internally. Also the Stream API is lazy, its elements are computed or fetched via network by demand. Collections are a in memory data store which means that every element in your Collection has to be computed and stored in your RAM before you can access it. But this does not mean that you can’t use streams for already computed data. It rather makes Streams more flexible than Collections in specific situations. You also can create Streams out of Collection based data structures as I will do in the following examples.
In the following I will show you some examples of the usage of Streams and I will also compare my solution to the classical programming approach.
filter
If you want to filter a list, you can just use filter
.
Let’s assume we have a list of words.
List<String> words = Arrays.asList("Abra", "Kadrabra", "Aloha");
Now we want to filter all words which starts with a capital A and print them.
words.stream()
.filter(word -> word.startsWith("A"))
.forEach(System.out::println);
We actually use two stream methods here. First we transform the list to a stream and filter and words which starts with a capital A. On the resulting stream we are printing out every word with System.out.println
.
You probably have seen similar lambda expressions like the on in the filter
before, but maye you have not seen many which I used in the forEach
method. System.out::println
is just syntactic sugar for following lambda expression: x -> System.out.println(x)
.
We can also write the code by not using streams by simply using a for loop:
for (String word : words) {
if (word.startsWith("A")) {
System.out.println(word);
}
}
This solution is in my opinion less elegant and can be written more expressive as I showed in the upper code snippet.
We can even improve our Stream example by creating a method for the startsWith
condition in a external method.
words.stream()
.filter(StreamExamples::startsWithA)
.forEach(System.out::println);
with
private static boolean startsWithA(String word) {
return word.startsWith("A");
}
sum
With the Stream API you also never have to write code like this again
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = 0;
for (Integer number : numbers) {
sum += number;
}
System.out.println(sum);
Instead you can write
System.out.println(numbers.stream().mapToInt(Integer::intValue).sum());
which does exactly the same as the upper code snippet.
map
map
projects every element of a stream into another form. As I used mapToInt
to project the element to Int’s, I can use map to project every element of a stream in to a new element.
numbers.stream()
.map(number -> number * number)
.forEach(System.out::println);
A traditional approach of writing this code would be following snippet:
for (Integer number : numbers) {
int squaredNumber = number * number;
System.out.println(squaredNumber);
}
flatMap
Next lets assume we have following data source:
List<List<Integer>> matrix = Arrays.asList(Arrays.asList(1, 2), Arrays.asList(3, 4), Arrays.asList(5, 6));
Our task is it now to sum up all element in the matrix.
To flat the data structure we can use flatMap
. It concatenates streams and generates a single stream as a result. So to compute the sum, we can use first flatMap
to concatenate multiple streams into one and then use mapToInt
and sum
as shown in the upper example of sum
.
System.out.println(matrix.stream().flatMap(Collection::stream).mapToInt(Integer::intValue).sum());
flatMap
allows use to abstain from for-loops in for-loops and write elegant code in one line.
Your colleagues will thank you if they pull your code.
collect
Last but not least you can transform a stream into a traditional data structure by using collect
.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> evenNumbers = numbers.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
I have uploaded all examples to my Github
Java 8 introduced Java Streams which allows us to express data processing queries in a short functional and elegant way. There are many more operations to explore, this post should just gave you an introduction to the Stream API. I hope you are motivated now to use the Stream API next time when you are using Java.
Written on January 20th, 2019 by Lasse Schultebraucks