Guide to Java 8 Comparator.comparing() – Java 8指南 比较器.比较()

最后修改: 2017年 3月 30日

1. Overview

1.概述

Java 8 introduced several enhancements to the Comparator interface, including a handful of static functions that are of great utility when coming up with a sort order for collections.

Java 8对Comparator接口引入了一些增强功能,包括一些静态函数,这些函数在为集合制定排序顺序时非常有用。

The Comparator interface can also effectively leverage Java 8 lambdas. A detailed explanation of lambdas and Comparator can be found here, and a chronicle on the applications of Comparator and sorting can be found here.

Comparator接口还可以有效地利用 Java 8 lambdas。关于lambdas和Comparator的详细解释可以在这里找到,而关于Comparator和排序的应用纪实可以在这里找到。

In this tutorial, we’ll explore several functions introduced for the Comparator interface in Java 8.

在本教程中,我们将探讨Java 8中为Comparator接口引入的几个函数

2. Getting Started

2.开始

2.1. Sample Bean Class

2.1.豆类样本

For the examples in this tutorial, let’s create an Employee bean and use its fields for comparing and sorting purposes:

在本教程的例子中,让我们创建一个Employee Bean并使用其字段进行比较和排序。

public class Employee {
    String name;
    int age;
    double salary;
    long mobile;

    // constructors, getters & setters
}

2.2. Our Testing Data

2.2.我们的测试数据

We’ll also create an array of employees that we’ll use to store the results of our type in various test cases throughout the tutorial:

我们还将创建一个雇员数组,在整个教程中,我们将用它来存储各种测试案例中的类型结果。

employees = new Employee[] { ... };

The initial ordering of elements of employees will be:

employees元素的初始排序将是。

[Employee(name=John, age=25, salary=3000.0, mobile=9922001), 
Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), 
Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

Throughout the tutorial, we’ll be sorting the above Employee array using different functions.

在整个教程中,我们将使用不同的函数对上述Employee数组进行排序。

For test assertions, we’ll use a set of pre-sorted arrays that we’ll compare to our sort results (i.e., the employees array) for different scenarios.

对于测试断言,我们将使用一组预先排序的数组,在不同情况下与我们的排序结果(即employees数组)进行比较。

Let’s declare a few of these arrays:

让我们声明一下这些数组中的几个。

@Before
public void initData() {
    sortedEmployeesByName = new Employee[] {...};
    sortedEmployeesByNameDesc = new Employee[] {...};
    sortedEmployeesByAge = new Employee[] {...};
    
    // ...
}

As always, feel free to refer to our GitHub link for the complete code.

一如既往,请随时参考我们的GitHub链接以获取完整代码。

3. Using Comparator.comparing

3.使用Comparator.comparing

In this section, we’ll cover variants of the Comparator.comparing static function.

在本节中,我们将介绍Comparator.comparing静态函数的变体。

3.1. Key Selector Variant

3.1.键选择器变体

The Comparator.comparing static function accepts a sort key Function and returns a Comparator for the type that contains the sort key:

Comparator.comparing静态函数接受一个排序键Function,并返回一个包含排序键的类型的Comparator

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

To see this in action, we’ll use the name field in Employee as the sort key, and pass its method reference as an argument of type Function. The Comparator returned from the same is used for sorting:

为了看到这个动作,我们将使用Employee中的name字段作为排序键,并将其方法引用作为Function.类型的参数传给Comparator,从同一返回的Comparator被用于排序。

@Test
public void whenComparing_thenSortedByName() {
    Comparator<Employee> employeeNameComparator
      = Comparator.comparing(Employee::getName);
    
    Arrays.sort(employees, employeeNameComparator);
    
    assertTrue(Arrays.equals(employees, sortedEmployeesByName));
}

As a result of the sort, the employees array values are in order by name:

作为排序的结果,employees数组的值是按名称排列的。

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), 
Employee(name=John, age=25, salary=3000.0, mobile=9922001), 
Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

3.2. Key Selector and Comparator Variant

3.2.关键选择器和比较器变体

There’s another option that facilitates overriding the natural ordering of the sort key by providing a Comparator that creates a custom ordering for the sort key:

还有一个选项,通过提供一个Comparator,为排序键创建一个自定义的排序,方便推翻排序键的自然顺序。

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

So let’s modify the test above. We’ll override the natural order of sorting by the name field by providing a Comparator for sorting the names in descending order as the second argument to Comparator.comparing:

所以让我们修改上面的测试。我们将覆盖按name字段排序的自然顺序,提供一个Comparator来按降序排序,作为Comparator.comparing的第二个参数。

@Test
public void whenComparingWithComparator_thenSortedByNameDesc() {
    Comparator<Employee> employeeNameComparator
      = Comparator.comparing(
        Employee::getName, (s1, s2) -> {
            return s2.compareTo(s1);
        });
    
    Arrays.sort(employees, employeeNameComparator);
    
    assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc));
}

As we can see, the results are sorted in descending order by name:

我们可以看到,结果是按姓名降序排序的。

[Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), 
Employee(name=John, age=25, salary=3000.0, mobile=9922001), 
Employee(name=Ace, age=22, salary=2000.0, mobile=5924001)]

3.3. Using Comparator.reversed

3.3.使用Comparator.reversed

When invoked on an existing Comparator, the instance method Comparator.reversed returns a new Comparator that reverses the sort order of the original.

当对现有的Comparator进行调用时,实例方法Comparator.reversed将返回一个新的Comparator,它将颠倒原有的排序顺序。

We’ll use the Comparator that sorts the employees by name and reverse it so that the employees are sorted in descending order of the name:

我们将使用按name排序的Comparator,并反转,使雇员按name的降序排序。

@Test
public void whenReversed_thenSortedByNameDesc() {
    Comparator<Employee> employeeNameComparator
      = Comparator.comparing(Employee::getName);
    Comparator<Employee> employeeNameComparatorReversed 
      = employeeNameComparator.reversed();
    Arrays.sort(employees, employeeNameComparatorReversed);
    assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc));
}

Now the results are sorted in descending order by name:

现在,结果是按姓名降序排序的。

[Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), 
Employee(name=John, age=25, salary=3000.0, mobile=9922001), 
Employee(name=Ace, age=22, salary=2000.0, mobile=5924001)]

3.4. Using Comparator.comparingInt

3.4.使用Comparator.comparingInt

There’s also a function, Comparator.comparingInt, which does the same thing as Comparator.comparing, but it takes only int selectors. Let’s try it with an example where we order employees by age:

还有一个函数,Comparator.comparingInt,,它与Comparator.comparing做同样的事情,但它只接受int选择器。让我们用一个例子来试试,我们按年龄来排列雇员

@Test
public void whenComparingInt_thenSortedByAge() {
    Comparator<Employee> employeeAgeComparator 
      = Comparator.comparingInt(Employee::getAge);
    
    Arrays.sort(employees, employeeAgeComparator);
    
    assertTrue(Arrays.equals(employees, sortedEmployeesByAge));
}

After the sort, the employees array values have the following order:

在排序之后,employees数组的值有以下顺序。

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), 
Employee(name=John, age=25, salary=3000.0, mobile=9922001), 
Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

3.5. Using Comparator.comparingLong

3.5.使用Comparator.comparingLong

Similar to what we did for int keys, let’s look at an example using Comparator.comparingLong to consider a sort key of type long by ordering the employees array by the mobile field:

与我们对int键所做的类似,让我们看一个使用Comparator.comparingLong的例子,通过对employees数组按mobile字段排序来考虑一个long类型的排序键。

@Test
public void whenComparingLong_thenSortedByMobile() {
    Comparator<Employee> employeeMobileComparator 
      = Comparator.comparingLong(Employee::getMobile);
    
    Arrays.sort(employees, employeeMobileComparator);
    
    assertTrue(Arrays.equals(employees, sortedEmployeesByMobile));
}

After the sort, the employees array values have the following order with mobile as the key:

在排序之后,employees数组的值有以下顺序,mobile为关键。

[Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), 
Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), 
Employee(name=John, age=25, salary=3000.0, mobile=9922001)]

3.6. Using Comparator.comparingDouble

3.6.使用Comparator.comparingDouble

Again, as we did for int and long keys, let’s look at an example using Comparator.comparingDouble to consider a sort key of type double by ordering the employees array by the salary field:

同样,就像我们对intlong键所做的那样,让我们看一个使用Comparator.comparingDouble的例子,通过对employees数组按salary字段排序来考虑double型的排序键。

@Test
public void whenComparingDouble_thenSortedBySalary() {
    Comparator<Employee> employeeSalaryComparator
      = Comparator.comparingDouble(Employee::getSalary);
    
    Arrays.sort(employees, employeeSalaryComparator);
    
    assertTrue(Arrays.equals(employees, sortedEmployeesBySalary));
}

After the sort, the employees array values have the following order with salary as the sort key:

在排序之后,employees数组的值有以下顺序,salary为排序键。

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), 
Employee(name=John, age=25, salary=3000.0, mobile=9922001), 
Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

4. Considering Natural Order in Comparator

4.考虑到比较器中的自然秩序

We can define the natural order by the behavior of the Comparable interface implementation. More information about the differences between Comparator and the uses of the Comparable interface can be found in this article.

我们可以通过Comparable接口实现的行为来定义自然顺序。关于Comparator之间的差异和Comparable接口的用途的更多信息可以在这篇文章中找到。

Let’s implement Comparable in our Employee class so that we can try the naturalOrder and reverseOrder functions of the Comparator interface:

让我们在我们的Employee类中实现Comparable,以便我们可以尝试Comparator接口的naturalOrderreverseOrder函数。

public class Employee implements Comparable<Employee>{
    // ...

    @Override
    public int compareTo(Employee argEmployee) {
        return name.compareTo(argEmployee.getName());
    }
}

4.1. Using Natural Order

4.1.使用自然秩序

The naturalOrder function returns the Comparator for the return type mentioned in the signature:

naturalOrder函数返回签名中提到的返回类型的Comparator

static <T extends Comparable<? super T>> Comparator<T> naturalOrder()

Given the above logic to compare employees based on the name field, let’s use this function to obtain a Comparator which sorts the employees array in natural order:

鉴于上述基于name字段比较雇员的逻辑,让我们使用这个函数来获得一个Comparator,它将employees数组按自然顺序排序。

@Test
public void whenNaturalOrder_thenSortedByName() {
    Comparator<Employee> employeeNameComparator 
      = Comparator.<Employee> naturalOrder();
    
    Arrays.sort(employees, employeeNameComparator);
    
    assertTrue(Arrays.equals(employees, sortedEmployeesByName));
}

After the sort, the employees array values have the following order:

排序后,employees数组的值有以下顺序。

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), 
Employee(name=John, age=25, salary=3000.0, mobile=9922001), 
Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

4.2. Using Reverse Natural Order

4.2.使用逆向自然秩序

Similar to how we used naturalOrder, we’ll use the reverseOrder method to generate a Comparator that will produce a reverse ordering of employees compared to the one in the naturalOrder example:

与我们使用naturalOrder的方式类似,我们将使用reverseOrder方法来生成一个Comparator,与naturalOrder例子中的employees相比,它将产生一个反向排序。

@Test
public void whenReverseOrder_thenSortedByNameDesc() {
    Comparator<Employee> employeeNameComparator 
      = Comparator.<Employee> reverseOrder();
    
    Arrays.sort(employees, employeeNameComparator);
    
    assertTrue(Arrays.equals(employees, sortedEmployeesByNameDesc));
}

After the sort, the employees array values have the following order:

在排序之后,employees数组的值有以下顺序。

[Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), 
Employee(name=John, age=25, salary=3000.0, mobile=9922001), 
Employee(name=Ace, age=22, salary=2000.0, mobile=5924001)]

5. Considering Null Values in Comparator

5.考虑比较器中的空值

In this section, we’ll cover the nullsFirst and nullsLast functions, which consider null values in ordering, and keep the null values at the beginning or end of the ordering sequence.

在这一节中,我们将介绍nullsFirstnullsLast函数,它们在排序时考虑null值,并将null值保持在排序序列的开始或结束。

5.1. Considering Null First

5.1.首先考虑空值

Let’s randomly insert null values in the employees array:

让我们在employees数组中随机插入null值。

[Employee(name=John, age=25, salary=3000.0, mobile=9922001), 
null, 
Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), 
null, 
Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

The nullsFirst function will return a Comparator that keeps all nulls at the beginning of the ordering sequence:

nullsFirst函数将返回一个Comparator,该函数将所有nulls保持在排序序列的开头。

@Test
public void whenNullsFirst_thenSortedByNameWithNullsFirst() {
    Comparator<Employee> employeeNameComparator
      = Comparator.comparing(Employee::getName);
    Comparator<Employee> employeeNameComparator_nullFirst
      = Comparator.nullsFirst(employeeNameComparator);
  
    Arrays.sort(employeesArrayWithNulls, 
      employeeNameComparator_nullFirst);
  
    assertTrue(Arrays.equals(
      employeesArrayWithNulls,
      sortedEmployeesArray_WithNullsFirst));
}

After the sort, the employees array values have the following order:

排序后,employees数组的值有以下顺序。

[null, 
null, 
Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), 
Employee(name=John, age=25, salary=3000.0, mobile=9922001), 
Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

5.2. Considering Null Last

5.2.考虑到最后的空值

The nullsLast function will return a Comparator that keeps all nulls at the end of the ordering sequence:

nullsLast函数将返回一个Comparator,它将所有nulls保留在排序序列的最后。

@Test
public void whenNullsLast_thenSortedByNameWithNullsLast() {
    Comparator<Employee> employeeNameComparator
      = Comparator.comparing(Employee::getName);
    Comparator<Employee> employeeNameComparator_nullLast
      = Comparator.nullsLast(employeeNameComparator);
  
    Arrays.sort(employeesArrayWithNulls, employeeNameComparator_nullLast);
  
    assertTrue(Arrays.equals(
      employeesArrayWithNulls, sortedEmployeesArray_WithNullsLast));
}

After the sort, the employees array values have the following order:

在排序之后,employees数组的值有以下顺序。

[Employee(name=Ace, age=22, salary=2000.0, mobile=5924001), 
Employee(name=John, age=25, salary=3000.0, mobile=9922001), 
Employee(name=Keith, age=35, salary=4000.0, mobile=3924401), 
null, 
null]

6. Using Comparator.thenComparing

6.使用Comparator.thenComparing

The thenComparing function lets us set up lexicographical ordering of values by provisioning multiple sort keys in a particular sequence.

thenComparing函数让我们通过提供多个特定序列的排序键来设置数值的词典式排序。

Let’s look at another array of the Employee class:

让我们看一下Employee类的另一个数组。

someMoreEmployees = new Employee[] { ... };

We’ll consider the following sequence of elements in the above array:

我们将考虑上述数组中的以下元素序列。

[Employee(name=Jake, age=25, salary=3000.0, mobile=9922001), 
Employee(name=Jake, age=22, salary=2000.0, mobile=5924001), 
Employee(name=Ace, age=22, salary=3000.0, mobile=6423001), 
Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

Then we’ll write a sequence of comparisons as the age followed by the name, and see the ordering of this array:

然后,我们将写一个比较序列,作为年龄,然后是名字,,看看这个数组的排序情况。

@Test
public void whenThenComparing_thenSortedByAgeName(){
    Comparator<Employee> employee_Age_Name_Comparator
      = Comparator.comparing(Employee::getAge)
        .thenComparing(Employee::getName);
  
    Arrays.sort(someMoreEmployees, employee_Age_Name_Comparator);
  
    assertTrue(Arrays.equals(someMoreEmployees, sortedEmployeesByAgeName));
}

Here the ordering will be done by age, and for the values with the same age, the ordering will be done by name. We can see this in the sequence we receive after sorting:

在这里,排序将按年龄进行,对于具有相同年龄的值,排序将按姓名进行。我们可以在排序后收到的序列中看到这一点。

[Employee(name=Ace, age=22, salary=3000.0, mobile=6423001), 
Employee(name=Jake, age=22, salary=2000.0, mobile=5924001), 
Employee(name=Jake, age=25, salary=3000.0, mobile=9922001), 
Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

Now we can use the other version of thenComparing, thenComparingInt, by changing the lexicographical sequence to name followed by age:

现在我们可以使用thenComparing的另一个版本,thenComparingInt,方法是将词法序列改为name,然后是age

@Test
public void whenThenComparing_thenSortedByNameAge() {
    Comparator<Employee> employee_Name_Age_Comparator
      = Comparator.comparing(Employee::getName)
        .thenComparingInt(Employee::getAge);
  
    Arrays.sort(someMoreEmployees, employee_Name_Age_Comparator);
  
    assertTrue(Arrays.equals(someMoreEmployees, 
      sortedEmployeesByNameAge));
}

After the sort, the employees array values have the following order:

在排序之后,employees数组的值有以下顺序。

[Employee(name=Ace, age=22, salary=3000.0, mobile=6423001), 
Employee(name=Jake, age=22, salary=2000.0, mobile=5924001), 
Employee(name=Jake, age=25, salary=3000.0, mobile=9922001), 
Employee(name=Keith, age=35, salary=4000.0, mobile=3924401)]

Similarly, the functions thenComparingLong and thenComparingDouble are for using long and double sorting keys, respectively.

同样,函数thenComparingLongthenComparingDouble分别用于使用longdouble排序键

7. Conclusion

7.结论

This article is a guide to several features introduced in Java 8 for the Comparator interface.

本文是关于Java 8中为Comparator接口引入的几个特性的指南。

As usual, the source code can be found over on Github.

像往常一样,源代码可以在Github上找到over