Why Comparator.comparing doesn't work with String::toLowerCase method reference?

loloof64 picture loloof64 · Apr 8, 2014 · Viewed 15.1k times · Source

I am trying to sort an array of Strings by reverse order (ignoring case), without modifying it, and just printing it. So I am using Java8 stream. But I can't manage to do it.

Here is my attempt :

package experimentations.chapter02;

import java.util.Arrays;
import java.util.Comparator;
import java.util.stream.Collectors;

public class StringStream {

    public static void main(String[] args) {
        sortStrings();
    }

    public static void sortStrings(){
        String[] stringsArray = "The quick brown fox has a dirty ladder".split("\\s+");
        System.out.println(
                Arrays.stream(stringsArray)
                .sorted(Comparator.comparing(String::toLowerCase).reversed())
                .collect(Collectors.toList())
        );
    }

}

The problem here is that String::toLowerCase is not accepted in the static method Comparator.comparing.

Meanwhile, I managed to sort the array, but modifying it:

public static void sortStrings(){
        String[] stringsArray = "The quick brown fox has a dirty ladder".split("\\s+");
        System.out.println(
                Arrays.stream(stringsArray)
                .map(String::toLowerCase)
                .sorted(Comparator.reverseOrder())
                .collect(Collectors.toList())
        );
}

So, what is the simpliest workaround?

Answer

nosid picture nosid · Apr 8, 2014

The problem is, that Java can not deduce the generic types for some complex expressions. The first statement works, whereas the second statement leads to a compile-time error:

Comparator<String> comparator = Comparator.comparing(String::toLowerCase);
Comparator<String> comparator = Comparator.comparing(String::toLowerCase).reversed();

There are several ways to solve the problem. Here are three of them:

Store the intermediate Comparator in a variable:

Comparator<String> comparator = Comparator.comparing(String::toLowerCase);
System.out.println(
            Arrays.stream(stringsArray)
            .sorted(comparator.reversed())
            .collect(Collectors.toList()));

Use String.CASE_INSENSITIVE_ORDER:

System.out.println(
            Arrays.stream(stringsArray)
            .sorted(String.CASE_INSENSITIVE_ORDER.reversed())
            .collect(Collectors.toList()));

Add explicit type parameters:

System.out.println(
            Arrays.stream(stringsArray)
            .sorted(Comparator.<String,String>comparing(String::toLowerCase).reversed())
            .collect(Collectors.toList()));