13

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?

Knowledge Craving
  • 7,955
  • 13
  • 49
  • 92
loloof64
  • 5,252
  • 12
  • 41
  • 78
  • I found the solution : .sorted((String e) -> e.toLowerCase) . Meanwhile, I've asked a deletion of the post, as it seems so obvious to me know, I could have found it without having asked the question ... – loloof64 Apr 08 '14 at 12:37
  • 2
    Your first code snippet compiles fine. Are you on an old build of Java 8? Or maybe compiling in eclipse? The problem you mention was fixed in build 129. See also http://stackoverflow.com/q/21751830/829571 – assylias Apr 08 '14 at 13:11

2 Answers2

26

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()));
nosid
  • 48,932
  • 13
  • 112
  • 139
  • The first code snippet posted by the OP compiles fine. – assylias Apr 08 '14 at 13:12
  • @assylias: About which code snippet are you talking? The first code snippet posted by the OP doesn't compile here. – nosid Apr 08 '14 at 13:38
  • `Arrays.stream(stringsArray).sorted(Comparator.comparing(String::toLowerCase).reversed()).collect(Collectors.toList())` compiles with javac/netbeans on b132. – assylias Apr 08 '14 at 13:39
  • 1
    @assylias: Well, it doesn't compile with the Java Compiler, that is part of the latest release of JDK8 by Oracle. – nosid Apr 08 '14 at 13:58
  • huh - that is weird. Can you `System.out.println(System.getProperty("java.runtime.version"));`? I have `1.8.0-b132`. – assylias Apr 08 '14 at 14:09
  • To be more precise, what compiles is `System.out.println(Arrays.stream(stringsArray).sorted(Comparator.comparing(String::toLowerCase).re‌​versed()).collect(Collectors.toList()));` – assylias Apr 08 '14 at 14:11
  • @assylias: I have exactly the same version installed. `java -version' prints `java version "1.8.0"; Java(TM) SE Runtime Environment (build 1.8.0-b132); Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)`. – nosid Apr 08 '14 at 14:17
  • I get an error that includes the message `"Object cannot be converted to Locale"`. `String::toLowerCase` is overloaded, with a no-arg version and a version that takes a `Locale` parameter. Apparently the compiler is having trouble figuring out that the no-arg version is the correct one. (I'm using the same build as @nosid.) – ajb Apr 08 '14 at 14:52
  • 1
    @assylias: I am not sure, why there is a compilation error at all. The rules regarding method references are quite complex. Therefore, I have created a new question: http://stackoverflow.com/questions/22945870/invalid-method-reference-for-overloaded-method-with-different-arities – nosid Apr 08 '14 at 18:58
7

I found the solution :

 .sorted((String e) -> e.toLowerCase) 

I think the problem with the syntax

 .sorted(String::toLowerCase)

is that the compiler then expects to pass an Object to the instance method toLowerCase of String. So I need to make my own lambda method, without ignoring the type of the lambda parameter (String), otherwise the compiler still can't resolve it.

loloof64
  • 5,252
  • 12
  • 41
  • 78
  • IntelliJ tells me that what you tried at first is a cyclic inference, which is forbidden. Now, I try to understand what is a cyclic inference :) – Arnaud Denoyelle Apr 08 '14 at 13:05
  • As telled me assylias, it seems that some IDE has not yet real support for Java8 features. Also, I tried a similar code inside Netbeans, without any problem. – loloof64 Apr 08 '14 at 15:51