How does this @Override for Arrays.sort work in Java?

Viewed 299
Arrays.sort(people, (n1, n2) -> (n2[0] == n1[0])?  n1[1] - n2[1] : n2[0] - n1[0]);

or

Arrays.sort(people,new Comparator<int[]>(){
    @Override
    public int compare(int[] n1, int[] n2){
        return (n2[0] == n1[0])? n1[1] - n2[1]: n2[0] - n1[0];
    }
});

Both these perform the same operation.

Input: [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]

Output: [[7,0],[7,1],[6,1],[5,0],[5,2],[4,4]]

I know the code is sorting it in groups but I don't understand how. I was similarly confused about PriorityQueue in Java:

PriorityQueue<Integer> pq = new PriorityQueue<>((a, b)-> b - a);

This one sorts in decreasing order.

Can someone explain it? Where can I study or read more about these "overrides" if there is any such material?

6 Answers

The arrow notation is a lambda function, short-hand for the same Comparator implementation. That's why you see the same results. It is not about @Override here, what you're asking for is how a Comparator works really.

A comparator orders 2 objects in the following order:

  • negative, sorts descending
  • zero, does nothing
  • pozitive, sorts ascending

So for the priority queue part, when the comparator sorts 1, 4, 6, 3, it compares the elements of the array and it swaps them if the difference is negative, e.g. it would swap 1 and 4, 4 and 6, etc.

For the first part of the question, you're using this implementation: (n2[0] == n1[0])? n1[1] - n2[1]: n2[0] - n1[0]

For 2-sized integer arrays, you're comparing the arrays as following.

  • If the first element of each array are not equal, you're trying to sort in descending order(i.e. bringing a [7, 0] ahead of a [4, 4])
  • If the first element of each array is equal, you're trying to sort in ascending order(i.e. bringing [7,0] ahead of [7,1]).

The javadoc of Comparator method compare(T o1, T o2) says:

Returns:
a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.

"Less than" and "greater than" refers to the sort order, not the numerical value, so if you want to sort descending, then 7 is "less than" 5.

So, the following shows how to sort ascending and descending:

// Ascending
(a, b) -> a - b

// Decending
(a, b) -> b - a

However, you should never use - minus operator for this, since it can cause overflow. Use the Integer.compare(int x, int y) method instead, or the equivalent methods on Long, Short, Byte, Double, and Float:

// Ascending
(a, b) -> Integer.compare(a, b)

// Decending
(a, b) -> Integer.compare(b, a)

Next part is that your code sorts by 2 fields, first field descending, second field ascending:

(o1, o2) -> {
    if (o1.a != o2.a)
        return Integer.compare(o2.a, o1.a); // sort by a (descending)
    return Integer.compare(o1.b, o2.b); // secondary sort by b (ascending)
}

Which in your code is done using a ternary operator:

(o1, o2) -> o1.a != o2.a ? Integer.compare(o2.a, o1.a) : Integer.compare(o1.b, o2.b)

Or rather the other way around:

(o1, o2) -> o1.a == o2.a ? Integer.compare(o1.b, o2.b) : Integer.compare(o2.a, o1.a)

@Override annotation just marks when a method from subclass overrides method from superclass. In your second example you are using something called anonymous inner class, you are basically passing an instance of a class, in your example Comparator which has an abstract method called compare. You are implementing that methods functionality in place, so that you don't have to create a new class which is extending Comparator class and so on.

Your first example is basically the same as second, but its syntax is shorter to write and looks cleaner, and easier to read. The second one is called Lambda expression.

Above Arrays.sort accept 2 params. The first is the list need to be sorted, the second is the Comparator. The Comparator is the functional interface that have method compare. Since it's the interface, so you need to implement it (@Override means that your implementation is overriding this method). The method compare allow you to decide the sort strategy (ascending, descending, blabla).

From the Comparator.compareTo javadoc https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#compare-T-T-

Compares its two arguments for order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.

So, what you see is expected. If we take two arguments from your input:
[7, 0], [4, 4]
the evaluation would be: 
n2[0] - n1[0] 
4 - 7 = -3 ( a negative integer)
It sees the first argument [7,0] is less than second argument [4,4] hence it is placed before in order.
Related