Fixing Java 8 Stream Gotchas with IntelliJ IDEA


Java 8 has been released almost one year ago in March 2014. At Pondus we’ve managed to update all of our production servers to this new version back in May 2014. Since then we’ve migrated major parts of our code base to lambda expressions, streams and the new Date API. We also use Nashorn to dynamically script parts of our application which may change during runtime.

The most used feature besides lambdas is the new Stream API. Collection operations are all around the place in almost any codebase I’ve ever seen. And Streams are a great way to improve code readability of all those collection crunching.

But one thing about streams really bothers me: Streams only provide a few terminal operations like reduce and findFirst directly while others are only accessible via collect. There’s a utility class Collectors, providing a bunch of convenient collectors like toList, toSet, joining and groupingBy.

For example this code filters over a collection of strings and creates a new list:

stringCollection
    .stream()
    .filter(e -> e.startsWith("a"))
    .collect(Collectors.toList());

After migrating a project with 300k lines of code to streams I can say that toList, toSet and groupingBy are by far the most used terminal operations in our project. So I really cannot understand the design decision not to integrate those methods directly into the Stream interface so you could just write:

stringCollection
    .stream()
    .filter(e -> e.startsWith("a"))
    .toList();

This might look like a minor imperfection at first but it gets really annoying if you have to use this kind of stuff over and over again.

There’s a method toArray() but no toList(). So I really hope some of the more convenient collectors will make it’s way into the Stream interface in Java 9. Brian? ಠ_ಠ

As a side note: Stream.js is a JavaScript port of the Java 8 Streams API for the browser and addresses the described issue nicely. All important terminal operations are directly accessible on the stream itself for convenience. See the API doc for details.

Anyways. IntelliJ IDEA claims to be the most intelligent Java IDE. So let’s see how we can utilize IDEA to solve this problem for us.

IntelliJ IDEA to the rescue

IntelliJ IDEA comes with a handy feature called Live Templates. If you don’t already know what it is: Live Templates are shortcuts for commonly used code snippets. E.g. you type sout + tabulator and IDEA inserts the code snippet System.out.println(). Read here to learn more about it.

How does Live Templates help with the problem described above? Actually we can simply create our own Live Templates for all the commonly used default Stream collectors. E.g. we can create a Live Template with the abbreviation .toList to insert the appropriate collector .collect(Collectors.toList()) automatically.

This is how it looks like in action:

Live Template Streams 1

Set up your own Live Templates

Let’s see how we can set this up. First go to Settings and choose Live Templates in the menu to the left. You can also use the handy filter input at the top left of the dialog.

Live Template Settings

Next we can create a new group called Stream via the + icon on the right. Next we add all of our stream-related Live Templates to this group. I’m using the default collectors toList, toSet, groupingBy and join quite commonly, so I create a new Live Template for each of those methods.

This part is important: After adding a new Live Template you have to specify the applicable context at the bottom of the dialog. You have to choose JavaOther. Afterwards you define the abbreviation, a description and the actual template code.

// Abbreviation: .toList
.collect(Collectors.toList())

// Abbreviation: .toSet
.collect(Collectors.toSet())

// Abbreviation: .join
.collect(Collectors.joining("$END$"))

// Abbreviation: .groupBy
.collect(Collectors.groupingBy(e -> $END$))

The special variable $END$ determines the cursors position after using the template, so you can directly start typing at this position, e.g. to define the joining delimiter.

Hint: You should enable the option “Add unambiguous imports on the fly” so IDEA automatically adds an import statement to java.util.stream.Collectors. The option is located in: EditorGeneralAuto Import

Let’s see those two templates in action:

Join

Live Template Streams 2

GroupBy

Live Template Streams 3

Live Templates in IntelliJ IDEA are an extremely versatile and powerful tool. You can greatly increase your coding productivity with it. Do you know other examples where Live Templates can save your live? Let me know!

Still not satisfied? Learn everything you ever wanted to know about Java 8 Streams in my Streams Tutorial.

Happy coding.

Read More