1

I want to sort an array which has two columns using a comparator on the second column and reverse the order.

The array is:

String[][] names = {{"a", "1234"}, {"b", "12312"}, {"c", "43"}};

And to return the sorted array I use:

String[][] out = Arrays.stream(names)
    .sorted(
        Comparator.comparing(x -> x[1])
    )
    .toArray(String[][]::new);

Which works well, but when I try to reverese the order of sort by using reversed():

String[][] out = Arrays.stream(names)
    .sorted(
        Comparator.comparing(x -> x[1]).reversed()
    )
    .toArray(String[][]::new);

I get error:

Error:(44, 66) java: array required, but java.lang.Object found

And my IDE highlights the x in the x[1] part. What I'm doing wrong?

Krzysztof Krasoń
  • 26,515
  • 16
  • 89
  • 115

1 Answers1

4

The compiler is not able to infer the generic type parameters to comparing() when you add the .reversed() chain call, so you have to explicitly give them.

First, you need to be aware of the full declaration of comparing():

static <T,U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T,? extends U> keyExtractor)

It has two type parameters:

  • T - The input to the function (lambda), i.e. String[] in your case
  • U - The return value of the function, i.e. String in your case

So you can write:

Comparator.<String[], String>comparing(x -> x[1]).reversed()

Alternatively, you can just specify the parameter type in your lambda (i.e. T), and the compiler will infer U.

Comparator.comparing((String[] x) -> x[1]).reversed()

That's probably the way you'd want to do it.


You can also do either of those to the first one, if you like. These are all the same, just becoming more and more explicit:

Comparator.comparing(x -> x[1])
Comparator.comparing((String[] x) -> x[1])
Comparator.<String[], String>comparing(x -> x[1])
Comparator.<String[], String>comparing((String[] x) -> x[1])
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • What causes the generic type inference to be lost? I also found using [`reverseOrder()`](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#reverseOrder--) combined with the chained [`comparing()`](https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#comparing-java.util.function.Function-java.util.Comparator-) works correctly without specifying any types. [Ideone Demo](https://ideone.com/bGu4V3) – 4castle Jul 05 '16 at 05:27
  • 1
    You'd have to look at the fine print of the inference rules in the Java Language Specification to answer that. Perhaps they'll refine them in future versions of Java to be able to infer for this scenarios as well, but until then, that's just how it is. If it infers, great. If not, add explicit types as shown here. – Andreas Jul 05 '16 at 07:44
  • 2
    @4castle: chaining `reverseOrder()` and `comparing()` makes no sense as the latter is a `static` method, thus the former has no impact at all. What does work is *nesting*, i.e. `Collections.reverseOrder(Comparator.comparing(x -> x[1]))`. Type inference does not work through chained method invocations, because the language designers feared the resulting complexity (the generic type can influence which overloaded method to invoke, which in turn can influence the inferred generic type). – Holger Jul 05 '16 at 10:21