Streams are quite fun - in short: it’s a different approach to writing loops. Suppose you have a List of Strings and you want to filter out the String starting with an ’s' and put them in a new List.
List<String> sStrings = new ArrayList<>();
for(int i = 0; i < strings.size(); i++){
if(strings.get(i).startsWith('s')){
//do something with strings starting with s
sStrings.add(strings.get(i));
}
}
Although still readable, there is a lot of redundant code: the for loop itself with the counter and condition, the if statement. With streams, it looks more readable.
List<String> sStrings = strings.stream()
.filter(s -> s.startsWith('s'))
collect(Collectors.toList());
Inside the filter method, you have a function - this has the same meaning as in mathematics: you have some inputs, the functions does it magic and something different comes out. In this case, the function takes a String and outputs true when the String starts with an ’s'.
This filter generates a stream on its own, in this case a stream of Strings all starting with an ’s'. You can collect the results, in this case a List.
Besides filter you also have map, suppose you want the length of all Strings starting with an ’s'. You can just add a map after the filter statement. map also takes a function, in this case a String as input, and it outputs a number, the length of the strings. You can string filter and map calls as much as you want. There is a second map, turning an in into an Integer. It is not mandatory as such with autoboxing available, but it shows you can have as much maps as you want.
List<Integer> stringLengths = strings.stream()
.filter(s -> s.startsWith('s'))
.map(s -> s.length())
.map(i -> new Integer(i))
collect(Collectors.toList());