1. 概述

在这个教程中,我们将讨论如何根据日期对列表中的对象进行排序。大多数排序技巧或示例都让用户按字母顺序对列表进行排序,但本文将探讨如何使用List(/java-collections)中的Date对象进行排序。

我们将利用Java的Comparator类来自定义排序列表的值

2. 准备工作

让我们看看在本篇文章中将使用的Employee实体:

public class Employee implements Comparable<Employee> {

    private String name;
    private Date joiningDate;

    public Employee(String name, Date joiningDate) {
        // ...
    }

    // standard getters and setters
}

我们可以注意到Employee类实现了Comparable接口。这个接口允许我们定义一个策略,用于比较同类型的对象之间的比较。这被用来按照自然顺序或由compareTo()方法定义的顺序对对象进行排序

3. 使用Comparable进行排序

在Java中,自然排序指的是如何在数组或集合中对基本类型或对象进行排序。java.util.Arraysjava.util.Collections包中的sort()方法应该是一致的,并反映相等性的语义。

我们将使用此方法来比较当前对象和传入的对象:

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

    @Override
    public boolean equals(Object obj) {
        return ((Employee) obj).getName().equals(getName());
    }

    @Override
    public int compareTo(Employee employee) {
        return getJoiningDate().compareTo(employee.getJoiningDate());
    }
}

这个compareTo()方法将比较当前对象与传递给它的对象。在上述例子中,我们比较当前员工的入职日期与传入的Employee对象。

3.1. 以升序排序

通常情况下,compareTo()方法描述了对象之间自然排序的比较逻辑。在这里,我们比较员工的入职日期字段与其他同类型的对象。如果两个员工的入职日期相同,它们将返回0:

@Test
public void givenEmpList_SortEmpList_thenSortedListinNaturalOrder() {
    Collections.sort(employees);
    assertEquals(employees, employeesSortedByDateAsc);
}

现在,Collections.sort(employees)会根据joiningDate而不是主键或名称对员工列表进行排序。我们可以看到,列表已根据员工的joiningDate进行排序,这成为了Employee类的自然顺序:

[(Pearl,Tue Apr 27 23:30:47 IST 2021),
(Earl,Sun Feb 27 23:30:47 IST 2022),
(Steve,Sun Apr 17 23:30:47 IST 2022),
(John,Wed Apr 27 23:30:47 IST 2022)]

3.2. 以降序排序

Collections.reverseOrder()方法按自然顺序的相反顺序对对象进行排序。它返回一个比较器,可以执行逆序排序。当对象在比较时返回null时,它将抛出NullPointerException

@Test
public void givenEmpList_SortEmpList_thenSortedListinDescOrder() {
    Collections.sort(employees, Collections.reverseOrder());
    assertEquals(employees, employeesSortedByDateDesc);
}

4. 使用Comparator进行排序

4.1. 以升序排序

现在,让我们使用Comparator接口实现来对员工列表进行排序。这里,我们将传递一个匿名内部类作为参数,即时传递给Collections.sort()API:

@Test
public void givenEmpList_SortEmpList_thenCheckSortedList() {

    Collections.sort(employees, new Comparator<Employee>() {
        public int compare(Employee o1, Employee o2) {
            return o1.getJoiningDate().compareTo(o2.getJoiningDate());
        }
    });

    assertEquals(employees, employeesSortedByDateAsc);
}

我们也可以将此语法替换为Java 8的Lambda语法,使代码更简洁,如下所示:

@Test
public void givenEmpList_SortEmpList_thenCheckSortedListAscLambda() {

    Collections.sort(employees, Comparator.comparing(Employee::getJoiningDate));

    assertEquals(employees, employeesSortedByDateAsc);
}

compare(arg1, arg2)方法接受两个泛型类型的参数并返回一个整数。由于它与类定义分离,我们可以根据不同的变量和实体定义自定义比较。当需要为比较对象定义不同的自定义排序时,这非常有用。

4.2. 以降序排序

我们可以通过反转员工对象的比较来按降序对给定的Employee列表进行排序,即比较Employee2Employee1。这将反转比较,从而返回降序结果:

@Test
public void givenEmpList_SortEmpList_thenCheckSortedListDescV1() {

    Collections.sort(employees, new Comparator<Employee>() {
        public int compare(Employee emp1, Employee emp2) {
            return emp2.getJoiningDate().compareTo(emp1.getJoiningDate());
        }
    });

    assertEquals(employees, employeesSortedByDateDesc);
}

我们还可以使用Java 8的Lambda表达式将上述方法转换为更简洁的形式。这将执行与上述函数相同的功能,只是代码行数比上述代码少。然而,这也使得代码不太易读。在使用Comparator时,我们在Collections.sort() API上即时传递了一个匿名内部类:

@Test
public void givenEmpList_SortEmpList_thenCheckSortedListDescLambda() {

    Collections.sort(employees, (emp1, emp2) -> emp2.getJoiningDate().compareTo(emp1.getJoiningDate()));
    assertEquals(employees, employeesSortedByDateDesc);
}

5. 总结

在这篇文章中,我们探讨了如何在Java集合中按日期对象的升序和降序对列表进行排序。

我们也简要介绍了Java 8的Lambda特性,这些特性在排序中很有用,可以帮助我们使代码更简洁。

如往常一样,本文中使用的完整代码示例可以在GitHub上找到。