Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 Comparator comparing doesn't chain

Let's say I have a Pair class

public class Pair<P, Q> {
    public P p;
    public Q q;


    public Pair(P p, Q q) {
        this.p = p;
        this.q = q;
    }

    public int firstValue() {
        return ((Number)p).intValue();
    }

    public int secondValue() {
        return ((Number)q).intValue();
    }
}

And I wish to sort it, first by first value, then by second value. Now' if I do this

List<Pair<Integer, Integer>> pairList = new ArrayList<>();
pairList.add(new Pair<>(1, 5));
pairList.add(new Pair<>(2, 2));
pairList.add(new Pair<>(2, 22));
pairList.add(new Pair<>(1, 22));
pairList.sort(Comparator.comparing(Pair::firstValue));

Everything works well and good, the list is sorted by first values of pair, but if I do this

pairList.sort(Comparator.comparing(Pair::firstValue).thenComparing(Pair::secondValue));

It fails with error

Error:(24, 38) java: incompatible types: cannot infer type-variable(s) T,U
(argument mismatch; invalid method reference
  method firstValue in class DataStructures.Pair<P,Q> cannot be applied to given types
    required: no arguments
    found: java.lang.Object
    reason: actual and formal argument lists differ in length)

Ok,so it might not be able to infer the arguments, so if I do this

pairList.sort(Comparator.<Integer, Integer>comparing(Pair::firstValue)
                                          .thenComparing(Pair::secondValue));

It fails with error

Error:(24, 39) java: invalid method reference
non-static method firstValue() cannot be referenced from a static context

Why does it work for comparing() and not for comparing().thenComparing() ?

like image 572
Mayur Kulkarni Avatar asked Feb 09 '17 03:02

Mayur Kulkarni


People also ask

How does Compare work in comparator Java?

The compare MethodIt returns a positive value if obj1 is greater than obj2. Otherwise, a negative value is returned. By overriding compare( ), you can alter the way that objects are ordered. For example, to sort in a reverse order, you can create a comparator that reverses the outcome of a comparison.

What is comparator chaining in Java?

A ComparatorChain evaluates every Comparator in the chain as needed. If the current Comparator returns a zero, the next Comparator is invoked until there are no more Comparator objects left in the chain. If the final Comparator returns a zero value, the ComparatorChain returns a zero.


2 Answers

It should be:

pairList.sort(Comparator.<Pair, Integer>comparing(Pair::firstValue)
                                       .thenComparing(Pair::secondValue));

First type parameter refers to the type being passed to Comparator. Second type parameter refers to the type that comparator should effectively compare with.

like image 100
theKidOfArcrania Avatar answered Sep 22 '22 15:09

theKidOfArcrania


The error seems to be related to Pair's generic parameters. One workaround it to use an explicit type, as you've attempted:

pairList.sort(Comparator.<Pair>comparingInt(Pair::firstValue).thenComparingInt(Pair::secondValue));
//                       ^^^^^^

Note the comparingInt() which reduces the number of parameters you need to specify, and improves performance by avoiding boxing.

Another solution is to parameterize the type reference:

pairList.sort(Comparator.comparingInt(Pair<?,?>::firstValue).thenComparingInt(Pair::secondValue));
//                                        ^^^^^
like image 23
shmosel Avatar answered Sep 21 '22 15:09

shmosel