Stream
Introduction Continued.
In
previous post we saw how we used to write imperative code i.e. we are telling HOW to do. We also saw how to write declarative
code i.e. we are telling WHAT to do.
Declarative code is must more concise and easily understandable.
Let
us revise what we did previously.
Below
is imperative code that we are used to write.
Get
names of Person whose age is greater than 25 sorted by name.
List<Person> qualifiedPeople = new ArrayList<Person>();
for (final Person person : people) {
if (person.age() > 25) {
qualifiedPeople.add(person);
}
}
Collections.sort(qualifiedPeople, new Comparator<Person>() {
@Override
public int compare(final Person person1,
final Person person2) {
return person1.name().compareTo(person2.name());
}
});
List<String> personName = new ArrayList<String>();
for(final Person person: qualifiedPeople){
personName.add(person.name());
}
So
the above code does the expected. But doesn’t it feel that it is too much hassle
to do. In SQL this would be so easy to do.
As
of Java 8 we have a better solution. Also we use Declarative programming i.e.
se say WHAT we want rather than
specifying HOW to do it. Let us look
at Java 8 code.
List<String> p =
listOfPeople().stream()
.filter(person -> person.age() > 25)
.sorted(Comparator.comparing(person
-> person.name()))
.map(person -> person.name())
.collect(Collectors.toList());
See
the code looks elegant. It is simple to understand and no clutter. In fact, we
can actually read the code. List, stream through and filter all person whose
age is greater than 25 and sort them by name and put them in a list. Isn’t it
awesome?
So
using Java 8 we are focusing only on our business logic and avoiding much of
boiler plate code.
Business
logic involves in database like operations like ordering, filtering, grouping,
etc. We see that we implement them again and again with fact that we generate
too much of Garbage. In the imperative style programming the List qualifiedPeople is used temporarily and then discarded.
We
know that Collections are used heavily in Java application. We store data in it
and then process it. How do we process it? Imperatively. We iterate through the
Collection and process each and every element in it. Let’s say that we have a
large Collection say a million elements.
·
So processing it
in serially does not make sense. You
have multiple cores on your machine why can’t you leverage it?
The
answer to the above question is STREAMS.
Stream
can help you chain complicated data processing while keeping your code readable
and intent clear.
Look
at the below diagram. We provide Lambda operation to filter, sorted, map and
collect. This are higher level of building blocks and so they don’t depend on
any specific threading models.
Figure: Forming a Stream pipeline by chaining Stream Operations.
Hence
their implementation can be single threaded or can be potentially utilize the
multicore architecture.
The
code becomes expressive with declarative style of code. So essentially stream
provide us following:
- Declarative style
of programming: More concise, more readable and more expressive.
- Composability: Assembling
the complex behavior by aggregating the simple behavior.
- Parallelizable:
Parallel programming can potentially lead to improved performance as it can
utilize the multiple cores.
- Streams are Lazy:
Computation is only performed when the terminal operation is initiated.