Java 8 Streams 101
Java 8 introduced a new Stream API. In this article we will take a look at how to create and use streams.
How do I create Streams?
Streams can be created using the of
method on the Stream class like this:
Stream<String> stream = Stream.<String>of("Lorem", "ipsum", "dolor", "sit", "amet");
Or calling the stream()
method on list or any collection like this:
List<String> list = Arrays.asList("Lorem", "ipsum", "dolor", "sit", "amet");
Stream<String> stream = list.stream();
Or from an Array like this:
String sentence = "Lorem ipsum dolor sit amet, consectetur adipiscing elit";
String[] tokens = sentence.split(" ");
Stream<String> stream = Arrays.stream(tokens);
Or you also use the Stream.Builder
:
Stream.Builder<String> builder = Stream.builder();
builder.add("lorem");
builder.add("ipsum");
builder.add("dolor");
builder.add("sit");
builder.add("amet");
Stream<String> stream = builder.build();
Stream Pipelines
Stream manipulate data in terms of pipelines. They have intermediate and terminal operations. Intermediate operations work on a stream, process it and return a stream. Example of such operations are map, filter, etc. Terminal Operations produce a side effect. Example of such operations are Collectors. Let’s take a deeper look in all these operations.
Let’s say we want to convert a list of string to lowercase. Earlier we use to do:
List<String> tokens = getTokens();
List<String> lowerCased = new ArrayList<>();
for (String token : tokens) {
lowerCased.add(token.toLowerCase());
}
With Streams, this can be rewritten in more readable way:
List<String> lowerCased = tokens.stream().map(String::toUpperCase).collect(Collectors.toList());
In this example to we first create a stream from the list/collection using stream()
method. Then we apply bunch of intermediate operations, map
in this case. Finally, we collect all the result in a list using collect
method.
What transformations can we apply?
There are many predefined transformations available - map, filter, limit, skip, sort, etc.
What are the terminal operations we can apply?
Terminal operations typically produce a result. You can have terminal operations to collect and convert the stream to any collections, or you can find some more information about the stream like - first, last, sum, findAny, distinct. If the operations is not present in the predefined operations, you can write a reducer function that reduces the entire stream to a single value by repeatedly applying a method.
To Vague. Show me some examples!
Filter Strings based on a condition
Find out Strings greater than 5 characters long
Before Streams
List<String> input = Arrays.asList("lorem ipsum dolor sit amet, consectetur adipiscing elit".split(" "));
List<String> filtered = new ArrayList<>();
for (String token : input) {
if (token.length() > 5) {
input.add(token)
}
}
After Streams
List<String> input = Arrays.asList("lorem ipsum dolor sit amet, consectetur adipiscing elit".split(" "));
List<String> out = input.stream()
.filter(it -> it.length() > 5)
.collect(Collectors.toList());
// out = [consectetur, adipiscing]