阐明 Lambda 表达式的语法

Clarifying syntax on Lambda Expression

我正在使用下面的代码按列对二维数组进行排序,它在第二个参数中采用了 lambda 表达式。我不太明白 (a,b) 是如何定义的以及 a[0] 和 b[0] 在比较器中是如何工作的。此代码排序正确,但我想了解语法。谢谢

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

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).

这意味着 lambda 表达式必须实现该方法,并且 myArrayInteger[][]int[][],这意味着 TInteger[]int[],所以 ab 都是 Integer[]int[]

因此,该语句将按第一列对二维数组进行排序。

2D array by column

Java 没有二维数组。它具有数组数组和一些语法糖,可让您声明一个数组数组并通过创建新数组和填充槽立即初始化外部数组。

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

是语法糖:

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]))

myArray 的类型是int[][]。也就是说,一个int[]的数组。因此,要对此进行排序,您需要提供一个 oracle:这个 oracle 需要能够对以下问题给出一致的答案:“给定 2 个组件(在这种情况下,组件是 int[]),告诉我是否第一个组件排序 'after' 第二个,或 'before' 第二个,或同等排序。通过 return 一个负数表示第一个是 'before',一个正数表示第一个的数字是 'after',0 表示它们相等或至少在相同级别的排序顺序。

通常,您可以通过创建 Comparator<int[]> 接口的实现、创建它的实例并将其传递来完成此操作:

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);

以上代码工作正常 - 您可以编译并 运行 它。

Lambda 与上面发生的事情没有太大区别。数组上只有一个相关的 sort 方法,它确实需要 2 个参数:一个 T 数组和一个可以比较 TComparator,T 是随心所欲(这里,T 是 int[])。那么为什么你可以在那里传递 lambda?

因为编译器是从外向内工作的。它最初只看到:

Arrays.sort(myArray, someLambdaExpressionIHaveNotLookedAtYet)

并且会就此停止,并尝试弄清楚要使用哪种方法。它会发现只有一种方法'works':public static <T> sort (T[] array, Comparator<T> comparator).

然后 javac 才继续:首先,它检查 Comparator 是否是所谓的 'single abstract method' 类型(SAM 类型):它是; Comparator 是一个接口,它只定义了一个方法*,这意味着它是一个 SAM 类型,这意味着您可以快捷地创建一个 class 那个 implements 这个接口,同时创建一个新的实例这个class,变成了一行。

现在,因为 java 已经知道 lambda 应该表示什么(即 Comparator<int[]> 的实现,并且它知道只有您尝试实现的一种方法(即 public int compare(int[] a, int[] b)),java 已经知道变量类型。您正在实现的方法 已经成为int[] a, int[] b

这就是您不必指定它的原因。你可以只写 (a, b) - java 为你填写 int[]。你可以写出来:试一试,写 Arrays.sort(myArray, (int[] a, int b[]) -> Integer.compare(a[0], b[0]) 你会发现它的工作方式完全一样。

剩下的很简单:-> 只是 lambda 语法,因为你没有在后面立即写 {,所以 -> 后面的单个表达式只是插入为 return值。

因此,

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

100% 等同于:

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

其中:

  • A = 随机选择的无关名称,
  • B = 预期的类型
  • C = B 中定义的一个方法的副本(如果没有一个方法,则不能使用此语法),
  • D = 你在 -> 之后写的任何内容。

*) 已在 java.lang.Object 中定义的方法不算数,具有默认实现的方法也不算数。