IntelliJ IDEA Inspection Settings for Refactoring to Java 8

I’ve been giving a talk this year showcasing how to use IntelliJ IDEA inspections to help you refactor existing code to Java 8. The initial version of this, the one I gave at DevoxxUK (video), is based on my Migrating to Java 8 Tutorial. I’ve also written before about how IntelliJ IDEA can help you write more idiomatic Java 8 code.

I’m revisiting the topic now that IntelliJ IDEA 2016.3 has added even more support for
identifying areas of code that can utilise Java 8 idioms and APIs, and making better use of those areas that already use Java 8. I’ve updated the presentation to use these new inspections, and performance tested the results when applied to a specific codebase. Let’s take a look at which inspections are used, how you configure them, and go into detail about what some of them do.


Here are the inspections that I’ve turned on for the presentation. Not all of them feature in
the specific code that I highlight, but they’re all valuable for locating areas of code that can
be migrated to Java 8.  We’ll look at a few of them in a bit more detail.

Firstly, in IntelliJ IDEA 2016.3 note that the “foreach loop can be collapsed with Stream API” inspection has been updated – see the checkbox in the bottom right? “Suggest to replace with forEach or forEachOrdered”?

Foreach Inspection Updated in IntelliJ IDEA 2016.3

By default this is not ticked. Which means code that originally was highlighted by this inspection when using 2016.2 may not be flagged in 2016.3 if you haven’t selected the checkbox. For example, IntelliJ IDEA will only give a warning about the code below if this checkbox is ticked:

If you apply the change, the code is simplified to use the forEach() method from Iterable, since Set implements Iterable

My preference is to inline variables where they’re not really adding anything (sometimes variable names are useful to document intermediate steps, but that’s not really necessary in this case), so the code simplifies further if you use Ctrl + Alt + N on the variable:

This inspection “foreach loop can be collapsed” identifies numerous places which use a traditional for loop but suggests a number of different alternatives to replace it with, including forEach (with and without the Streams API), and collect. This is covered in more detail in the Migrating to Java 8 Tutorial.

This inspection was around in IntelliJ IDEA 2016.2, but has been updated in 2016.3 – not only with the ability to turn on or off the suggestion to use forEach, but also to provide better alternatives to refactor to, and suggest new places that can use the Streams API.

By better alternatives, I mean that IntelliJ IDEA has become even smarter with how it refactors code. Take a look at this example:

In IntelliJ IDEA 2016.2, this would have been refactored to:

But in IntelliJ IDEA 2016.3 both for loops are folded into the stream operation via flatMap

In this specific case the code is still not a perfect example of using streams, as we have a if/else which cannot be folded into the stream, and an initial null check. Also remember that although using forEach is a good first step towards understanding and using streams, it’s often a sign that the operation could perhaps be redesigned to use a more efficient stream operation that doesn’t need to process every item.

Another example of the improvements is how IntelliJ IDEA has become smarter about identifying code that sorts Collections, and suggesting alternatives. For example, this is typical pre-Java-8 code:

Previously, IntelliJ IDEA suggested this could be simplified to:

It took a vigilant developer to notice that the sort method can actually be folded into the stream operation. Now, IntelliJ IDEA does this for you:


As well as improved suggestions for refactoring, IntelliJ IDEA 2016.3 also identifies more areas that can use Stream operations. In particular, it can suggest that areas that iterate over arrays or use indexed loops can be refactored. For example, consider this code that loops over an array:

The inspection suggests that this can be refactored to:

We can refactor more to improve the readability further. Firstly by inlining the validFields  variable, and then by extracting a method for the complex filter logic. This final step has the nice side effect of letting us remove the comment, since the method name combined with the filter is descriptive enough to show what the code is trying to do

But you do need to take care with refactoring code like this. The updated code is easier to read, as it expresses the intention nicely, but iterating over arrays with a traditional for loop is an operation that is efficient for computers. Using to turn the array into a Stream will probably have some performance impact. In the grand scheme of things this is likely to be negligible, but if performance matters in your application you should test this impact.

One of the new inspections available in 2016.3 is the ability to locate places where Collections.removeIf  can be used. If, for example, you have a Set  of values and you want to remove some items that meet some criteria, you may have code that looks like this:

The “Loop can be replaced with Collection.removeIf” inspection suggests this can be refactored to:

This code is clearly much simpler, the boilerplate for managing the iteration has disappeared and we’re left with only the condition to check each item against.

Another new inspection is around Comparators. Of course, Comparators can now be implemented as lambda expressions rather than the traditional anonymous inner class, but what is less obvious is that there are new helpers on the Comparator class than can simplify many of these even further. Consider the following code:

The “Use Comparator combinators” inspection suggests we change this to

It’s less code, but more usefully the important information now stands out more – the Constraint values in the TreeSet will be ordered by their Level.

There are more new inspections, and more examples of improvements to the existing inspection suggestions. To see some of those mentioned here in action, and some more new ones, check out the Java 8 Inspections Screencast.

And to see these inspections being used against a real code base, with details of the performance implications of these changes, take a look at my Refactoring to Java 8 presentation from Devoxx Belgium last month

Whether you’re only just considering enabling the Java 8 inspections, or have previously checked them out but not felt compelled to use them, do take a look at them in IntelliJ IDEA 2016.3, you may be surprised at how much they can simplify your code.

This entry was posted in Tips & Tricks and tagged , , . Bookmark the permalink.

11 Responses to IntelliJ IDEA Inspection Settings for Refactoring to Java 8

  1. Tagir Valeev says:

    urls.removeIf(url -> url.getPath().endsWith("jnilib"));

    This version is not only simpler. For some collections (notably ArrayList) it’s also much faster. When you use iterator.remove() in the loop, every removal shifts the whole underlying array to the left, so you have ~O(listSize*numberOfDeletions) complexity. However if you replace it with removeIf, the optimized implementation works which can remove all the elements shifting only once, lowering the complexity to ~O(listSize+numberOfDeletions).

    • Dean Wette says:

      Is it really correct to say the complexity is O(listSize*numberOfDeletions)? In reality ArrayList.remove() uses System.arrayCopy(), a native method that might well use something like memcpy/memmove to shift the array as single bulk operation, rather than an element at a time, like you might do with a pure Java approach. Then the shift is an O(1) operation and the complexity of removing M elements is O(M), not O(M*N) = O(N^2).

  2. machak says:

    Font size and color scheme for code samples are pretty much unreadable :(

    • Trisha Gee says:

      I’m sorry to hear that. I don’t see the same problem, how are you viewing the post? Mobile or desktop? Which browser?

  3. machak says:

    I can read the code samples but I need to focus really hard..and by focus I don’t mean my mind but my eyesight 😉
    Font is just too small and color difference between text/even numbered lines isn’t that clear…

    Opera 41.0.2353.69 – (x86_64; Linux)

    • Trisha Gee says:

      I’m not sure I have much control over how it’s displayed if I’m honest. But I’ll look into it and see what I can find.

  4. Rakesh says:

    @Trisha, even though i have followed the steps to enable inspection for Java 8 Lambdas refactoring, Analyze -> Inspect code does not show any WARNINGS. I am using project “morphia” in one of you tutorial. my intellij idea version is

    IntelliJ IDEA 2016.3.5
    Build #IU-163.13906.18, built on March 6, 2017

    • Trisha Gee says:

      I’m not sure exactly what the problem might be, but can you try:
      – Setting the project language level to Java 8 (check it’s set to 8 in every module too – because Morphia uses Gradle, and Gradle sets the language level to 6, this means IntelliJ tends to set the language level to 6 on each of the individual modules, overriding the project level).
      – Making sure that under the “foreach loop can be collapsed with Streams API” inspection, you’ve ticked the “suggest to replace with forEach”. You might even need to tick the “replace trivial foreach statements” too, but I don’t think this should be required.

  5. Gunjit says:

    Hi Trisha,

    I am facing an issue where replace with forEach is replacing without the Streams API. Also I am unable to use replace with count. Intellij in not suggesting that.

    Project language level is set to Java 8.
    Under the “foreach loop can be collapsed with Streams API” inspection, I’ve ticked the “suggest to replace with forEach”.

    Is there something I am missing ?

  6. Chris Hodges says:

    IntelliJ (2017.3.3) suggests stream optimization for subsequent operations that are not semantically identical. For example, when I have a stream that produces a List by collecting via .collect(Collectors.toList()) and then pass this list to a new ArrayList() in constructor to have a *mutable copy* to further modify the list, it suggests that this operation can be omitted.

    The API-Spec of Collectors clearly says that “There are no guarantees on the type, mutability, serializability, or thread-safety of the List returned;”. While the *current implementation* does use an ArrayList for the created backing list implementation, it is wrong to assume mutabilty of anything returned from the default collectors in the java Collectors package in future versions.

    • Tagir Valeev says:

      Thank you for your report. An issue is created: IDEA-185812. Note that copying is suboptimal. Instead Collections.toCollection(ArrayList::new) could be used (which is just what’s performed currently inside Collections.toList()).

      We will add an option, though I’m not sure whether this should be a default one. It’s unlikely that the toList() behavior will be ever changed. Note that in Java 10 separate toUnmodifiableList() will be added which explicitly returns an unmodifiable list.

Leave a Reply

Your email address will not be published. Required fields are marked *