简体   繁体   中英

Clarifying syntax on Lambda Expression

I am using below code to sort a 2D array by column, it is taking a lambda expression in the second parameter. I don't quite understand how (a,b) is defined and how a[0] and b[0] is working in the comparator. This code is sorting correctly, but I want to understand the syntax. Thanks.

   Arrays.sort(myArray, (a, b) -> Integer.compare(a[0], b[0]))

2D array by column

Java doesn't have 2D arrays. It has arrays of arrays, and some syntax sugar to let you declare an array-of-arrays and immediately initialize the outer array by creating new arrays and filling the slots.

int[][] grid = new int[10][5];

is syntax sugar for:

int[][] grid = new int[10][];
for (int i = 0; i < 10; i++) grid[i] = new int[5];

Arrays.sort(myArray, (a, b) -> Integer.compare(a[0], b[0]))

The type of myArray is int[][] . In other words, an array of int[] . Thus, to sort this, you need to provide an oracle: This oracle needs to be capable of giving consistent answers to the question: "Given 2 components (in this case, the component is int[] ), tell me if the first component sorts 'after' the second, or 'before' the second, or sorts equally. Do so by returning a negative number to indicate that the first is 'before', a positive number to indicate the first is 'after', and 0 to indicate they are equal or at least sort-ordering-wise at an equal level.

Ordinarily, you'd do this by creating an implementation of the Comparator<int[]> interface, creating an instance of this, and passing it along:

class MySorter implements Comparator<int[]> {
    @Override public int compare(int[] a, int[] b) {
        return Integer.compare(a[0], b[0]);
    }
}

MySorter mySorter = new MySorter();
Arrays.sort(myArray, mySorter);

The above code works fine - you can compile and run it.

Lambdas aren't much different from what is happening above. There is only one relevant sort method on Arrays, and it indeed does take 2 arguments: An array of T , and a Comparator that can compare T , with T being whatever you please (here, T is int[] ). So why can you pass a lambda there?

Because the compiler works outside-in. It initially just sees:

Arrays.sort(myArray, someLambdaExpressionIHaveNotLookedAtYet)

and will stop right there, and try to figure out which method is intended. It'll find that only one method 'works': public static <T> sort (T[] array, Comparator<T> comparator) .

Only then does javac continue: First, it checks that Comparator is a so-called 'single abstract method' type (a SAM type): It is; Comparator is an interface that defines just a single method*, which means it is a SAM type, which means you can shortcut the creation of a class that implements this interface, along with creating a new instance of this class, into a one-liner.

Now, because java already knows what the lambda is supposed to represent (namely, an implementation for Comparator<int[]> , and it knows there is only one method that you're trying to implement (namely, public int compare(int[] a, int[] b) ), java already knows the variable types . The method you're implementing has to be int[] a, int[] b .

That's why you don't have to specify it. You can just write (a, b) - java fills in the int[] for you. You CAN write it out: Give it shot, write Arrays.sort(myArray, (int[] a, int b[]) -> Integer.compare(a[0], b[0]) and you'll find it works exactly the same way.

The rest is trivial: -> is just lambda syntax, and because you don't write a { immediately afterwards, the single expression that follows the -> is just inserted as return value.

Thus,

(a, b) -> Integer.compare(a[0], b[0])

is 100% equivalent to:

public class A implements B {
    public C {
        return D;
    }
}
A a = new A();

Where:

  • A = a randomly chosen irrelevant name,
  • B = the type that is expected there
  • C = a copy of the one method defined in B (and if there is not one method, you can't use this syntax),
  • D = whatever you wrote after the ->.

*) Methods that are already defined in java.lang.Object don't count, and methods with default impls also don't count.

Arrays.sort(T[] a, Comparator<? super T> c) takes a Comparator<T> as the second argument, which declares the method int compare(T o1, T o2) .

That means the lambda expression must implement that method, and with myArray being an Integer[][] or int[][] , it means that T is an Integer[] or int[] , so both a and b are Integer[] or int[] .

The statement will therefore sort the 2D array by the first column.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM