Java 8 Stream on Android
In this article you’ll learn what Streams are and how to use them in Android development, even if, in theory, you cannot use them in Android development. O_O
Since the Java 8 release (march 2014), android developers are struggling to find a way to replicate all (or at least some) of the new awesome patterns provided by Java 8.
Unfortunately Android doesn’t support Java 8 yet; Java 7 (v1.7) is supported on the KitKat edition (Android 4.4.+) and above.
So farewell Lambda expressions…
Of course not.
Some brilliant minds came with a super solution to this stumbling block:
From now on even sad lambda-less android developer like us could use the arrow ‘->’ method in our code!
HOW TO INSTALL RETROLAMBDA
As a warm up, let’s see quickly how to use Retrolambda inside our projects (it’s assumed that you know the AndroidStudio gradle build system and how it works):
- In the ./build.gradle file add a new classpath:
2. In the ./app/build.gradle file apply the Retrolambda plugin:
3. Inside this file, also add:
4. Sync the Project
Now you’re ready to use all the lambdas in the world! NOT BAD.
Ok, retrolambda bla bla bla…but what is a Stream?
A stream is an abstraction for specifying aggregate computations on a DataSet
Basically streams allow us to write collections-processing code at a higher level of abstraction using a functional approach.
A common pattern for Java developers when working with collections is to iterate over a collection, operating on each element in turn. We do that on a daily bases: in my life I thin I wrote the ‘for’ word on the IDE much more times than I ate a pizza (and I’m italian!). So iterate! Iterate! ITERATE!
For (ah-ah-ah) example, if we want to find John Snow in all of the characters of the ‘A Song of Ice and Fire’ books and set that “He knows nothing”, we would write the code below:
Of course it involves a lot of boilerplate code. Every time you want to iterate over a collection, filtering it and do stuff on each item, you had to write this ugly code. In addition, the for-loop structure is difficult to comprehend until we reach the body of the loop. For a single loop things are fine, but when you have multiple loops it becomes hard to understand.
How to make the code more readable and easy to write?
This is where the Java 8 Stream interface comes into play.
But, wait, we’re talking about Android (only Java 7 remember?). :(
Fortunately, another clever developer rolled up his sleeves and released a Java 5, 6, 7 porting of Streams: Lightweight-Stream-API
Just add this line to your ./app/build.gradle file...
…and voilà, le jeux sont fait.
Let’s see how Stream works.
Much more cleaner than before!
The Stream API introduces — roll the drums — the concept of Stream class.
What we do before is called a STREAM PIPELINE, which is composed of three distinct parts:
1. a source (the Collection -> Stream conversion line of code)
2. zero or more intermediate operations (filter, map, distinct, sorted)
3. a terminal operation
The ‘intermediate operations’ are lazy because they don’t execute anything until the terminal operation starts. They just set up the pipeline.
The Stream code example is very clean but we can do better and take advantage of Lambda expression’s Type inference. Type inference enables a compiler to deduce the type of a particular expression automatically when it compiles your code, simply by examining the values you provide.
Here’s the new code:
The class name inside operations are gone!
For anyone who has just woken up and can’t spot the differences we are passed from “(Person person) -> …” to “person -> …”. This is the magic of type inference.
Maybe the most difficult part of the Stream pattern is to understand what all the intermediate operations do. Some of them are intuitive (filter, distinct, sorted), others may lead to a bit of headache.
I’m confident that you have been writing map operations many times without recognize them.
Every time you take a list of Integer and converts them to their String equivalent, you are performing a map operation.
A precise definition of map is:
If you’ve got a function that converts a value of one type into another, map lets you apply this function to a stream of values, producing another stream of the new values. (Java 8 Lambdas — Richard Warburton)
Sometimes you want a different type of map in which you create a new Stream object as the replacement, instead of a new value: this is where flatMap could helps.
Above you can observe a so-called “marble diagram”, a graphic representation of the stream. This kind of diagram was invented by the developers behind the ReactiveX project. These workflows are born to explain the RxJava Observables-Observer process but we can use it for the flatMap explanation. This intermediate operation allows to replace a value with a Stream and chains all the streams together.
Here we convert the stream of Person items in a Stream of Crow items with flatMap. After that we convert the stream into a List of Crow items.
BONUS JAVA8 FEATURE
In some cases, we may have the need to reuse a filtering function, a mapping function or a sorting function throughout the code.
Here is the moment when an other feature of Java 8 comes in handy: the :: method reference.
Let’s say we had to compare the Night’s Watch rangers multiple times in multiple sorting operations. An easy solution is to replicate always the same code in every sorted() operation.
But there’s a better way to do this. Look at example below:
First we create a method in the class we want to use the streams.
Once you did that, you can replace the sorted() content with:
With the class::method notation we can reuse our functions in an easy way.
JAVA 8 vs Lightweight-Stream-API (LSA)
Although Java 8's stream and its LSA counterparts work in the same way under the hood, there are some slightly differences between them.
In the examples above you can see that the creation of the stream starts with:
In the Java 8 implementation you’ll see YourCollection.stream(…) instead.
Either way an instance of Stream<YourCollectionItem> is created.
Another small difference concernes the sorting operation. In Java 8, the operation uses the function ‘sort()’. In Lightweight-Stream-API, the function is called ‘sorted()’ but they work exactly in the same way.
ZOOMING BACK OUT
That’s it. You have reach the bottom of this TL;DR article.
I described only some of the features of Java 8 and LSA. I’ve to study this subject because every day I found something new and awesome. There are so many enhancement to learn and try in our code!
I’d love to hear about how you’ve implemented some Stream operations, anything new you have discovered about these arguments and any questions that this article rise up.