何时使用Comparable和Comparator

123

我有一个对象列表,需要按照某个字段(例如Score)进行排序。我写了一个新的实现Comparator接口的类来完成任务,并且它可以工作。

现在回过头想,我在想是否应该让我的类实现Comparable接口,而不是创建一个实现Comparator接口的新类。分数是对象排序的唯一字段。

  1. 我所做的做法是否可接受?

  2. 正确的方法是“首先使类实现Comparable接口(自然排序),如果需要替代字段比较,则创建一个实现Comparator接口的新类”吗?

  3. 如果(2)是正确的,那么这是否意味着在我拥有原始类的情况下,只有在类实现Comparable接口之后才应该实现Comparator接口?

19个回答

138

4
这是一个技术性解释,且非常准确,但它并没有真正说明最佳实践。 - extraneon
46
告诉我们什么时候使用每一个选项 - 如果这不是最佳实践,那还有什么是最佳实践呢? - Bozho
2
实现Comparable接口是否意味着我正在定义自然顺序?这给了我我正在寻找的答案。谢谢 :) - Somjit

84

如果使用自然排序是该类的明显方式,并且任何需要排序该类的人通常都希望以这种方式排序,那么我会说该对象应实现Comparable。

然而,如果排序是该类的不寻常用途,或者仅对特定用例有意义,则Comparator是更好的选择。

换句话说,根据类名,可否清楚地知道如何使用Comparable进行排序?还是必须查阅Java文档?如果是后者,则每个未来的排序用例都需要使用Comparator,在这种情况下,实现Comparable可能会降低类的用户的速度,而不是提高速度。


你能否举个快速的例子? - rgamber
有一个使用ComparableVersion的Version类。 Version - 提供工厂方法 ComparableVersion应该是一个对象(没有静态方法)-提供了一个可以与另一个版本进行比较的版本。 责任被分开了。 - ses
2
请参考以下网址:http://java-journal.blogspot.in/2011/01/when-to-use-comparable-and-when-to-use.html。 - a Learner
面试官问我为什么要使用Comparator,当使用Comparable也可以完成相同的任务时,而我却无言以对 :( - Aadam
@aLearner 的链接已失效。 - G.Brown
@G.Brown 使用 HTTPS。 - a Learner

63

使用 Comparable

  • 如果对象在您的控制范围内。
  • 如果比较行为是主要的比较行为。

使用 Comparator

  • 如果对象不在您的控制范围内,您无法让它们实现 Comparable
  • 当您需要与默认行为(由 Comparable 指定)不同的比较行为时。

32

Comparable - java.lang.Comparable: int compareTo(Object o1)

可比较对象能够将自身与另一个对象进行比较。该类本身必须实现java.lang.Comparable接口才能够比较其实例。

  • 能够比较当前对象和提供的对象。
  • 使用它,我们可以基于实例属性实现仅一种排序序列。 例如:Person.id
  • 一些预定义类(如String、Wrapper类、Date、Calendar)已经实现了Comparable接口。

Comparator - java.util.Comparator: int compare(Object o1, Object o2)

比较器对象能够比较两个不同的对象。该类没有比较其实例,而是一些其他类的实例。该比较器类必须实现java.util.Comparator接口。

  • 能够比较同类型的任意两个对象。
  • 使用它,我们可以基于实例属性实现多个排序序列并给每个序列命名。 例如:Person.id,Person.name,Person.age
  • 我们可以为我们的预定义类实现Comparator接口以进行自定义排序。

示例:

public class Employee implements Comparable<Employee> {

    private int id;
    private String name;
    private int age;
    private long salary;

    // Many sort sequences can be created with different names.
    public static Comparator<Employee> NameComparator = new Comparator<Employee>() {         
        @Override
        public int compare(Employee e1, Employee e2) {
            return e1.getName().compareTo(e2.getName());
        }
    };
    public static Comparator<Employee> idComparator = new Comparator<Employee>() {       
        @Override
        public int compare(Employee e1, Employee e2) {
            return Integer.valueOf(e1.getId()).compareTo(Integer.valueOf(e2.getId()));
        }
    };

    public Employee() { }
    public Employee(int id, String name, int age, long salary){
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    // setters and getters.

    // Only one sort sequence can be created with in the class.
    @Override
    public int compareTo(Employee e) {
    //return Integer.valueOf(this.id).compareTo(Integer.valueOf(e.id));
    //return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
        if (this.id > e.id) {
            return 1;
        }else if(this.id < e.id){
            return -1;
        }else {
            return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
        }

    }   

    public static void main(String[] args) {

        Employee e1 = new Employee(5, "Yash", 22, 1000);
        Employee e2 = new Employee(8, "Tharun", 24, 25000);

        List<Employee> list = new ArrayList<Employee>();
        list.add(e1);
        list.add(e2);
        Collections.sort(list); // call @compareTo(o1)
        Collections.sort(list, Employee.nameComparator); // call @compare (o1,o2)
        Collections.sort(list, Employee.idComparator); // call @compare (o1,o2)
    }
}
  • 对于自定义排序,我们使用比较器 @compare(o1, o2),对于其他情况,我们使用可比接口 @compareTo(o1)。如果要按多个字段进行排序且不更改代码,则使用比较器。

请参考我的帖子Java 8 Lambda:Comparator


13

当你比较同一类实例时,应该使用Comparable。

Comparator可用于比较不同类的实例。

需要为其对象定义自然排序的类实现Comparable。例如,String实现了Comparable接口。

如果需要不同的排序顺序,则实现comparator并定义其自己的比较两个实例的方式。


12

如果需要按自然顺序对对象进行排序,则使用Comparable,而如果需要根据不同对象的属性进行排序,则在Java中使用Comparator。

Comparable和Comparator的主要区别:

+------------------------------------------------------------------------------------+
¦               Comparable                ¦                Comparator                ¦
¦-----------------------------------------+------------------------------------------¦
¦ java.lang.Comparable                    ¦ java.util.Comparator                     ¦
¦-----------------------------------------+------------------------------------------¦
¦ int objOne.compareTo(objTwo)            ¦ int compare(objOne, objTwo)              ¦
¦-----------------------------------------+------------------------------------------¦
¦ Negative, if objOne < objTwo            ¦ Same as Comparable                       ¦
¦ Zero,  if objOne == objTwo              ¦                                          ¦
¦ Positive,  if objOne > objTwo           ¦                                          ¦
¦-----------------------------------------+------------------------------------------¦
¦ You must modify the class whose         ¦ You build a class separate from to sort. ¦
¦ instances you want to sort.             ¦ the class whose instances you want       ¦
¦-----------------------------------------+------------------------------------------¦
¦ Only one sort sequence can be created   ¦ Many sort sequences can be created       ¦
¦-----------------------------------------+------------------------------------------¦
¦ Implemented frequently in the API by:   ¦ Meant to be implemented to sort          ¦
¦ String, Wrapper classes, Date, Calendar ¦ instances of third-party classes.        ¦
+------------------------------------------------------------------------------------+

10

Comparator可以做Comparable所能做的一切,而且更多。

| | Comparable | Comparator ._______________________________________________________________________________ 用于允许Collections.sort工作 | 是 | 是 可以比较多个字段 | 是 | 是 存在于你正在比较的类中,并作为“默认”比较方式 | 是 | 是 可以存在于你正在比较的类之外 | 否 | 是 可以有多个具有不同方法名称的实例 | 否 | 是 输入参数可以是列表 | 仅对象 | 任何类型 可以使用枚举 | 否 | 是

我发现使用匿名类作为比较器的最佳方法如下:

private static void sortAccountsByPriority(List<AccountRecord> accounts) {
    Collections.sort(accounts, new Comparator<AccountRecord>() {

        @Override
        public int compare(AccountRecord a1, AccountRecord a2) {
            return a1.getRank().compareTo(a2.getRank());
        }
    });
}

您可以在计划进行排序的类中创建多个版本的这些方法。因此,您可以拥有:

  • sortAccountsByPriority
  • sortAccountsByType
  • sortAccountsByPriorityAndType

    等等...

现在,您可以在任何地方使用这些排序方法并获得代码重用。这为我提供了与可比较对象相同的一切,甚至更多...所以我不认为有任何理由使用可比较对象。


8

我认为:

  • 如果比较是直观的,那么一定要实现Comparable
  • 如果不确定比较是否直观,请使用Comparator,因为它更明确,对于维护代码的可怜人来说更清晰
  • 如果有多个直观的比较可能性,我会选择使用Comparator,可能是在要比较的类中构建一个工厂方法。
  • 如果比较是特殊目的,请使用Comparator

7
以下几点可以帮助您决定在哪些情况下应该使用Comparable,以及在哪些情况下应该使用Comparator:
1)代码可用性
2)单一排序标准与多个排序标准
3)Arrays.sort()和Collection.sort()
4)作为SortedMap和SortedSet中的键
5)更多类与灵活性
6)类间比较
7)自然顺序
如需更详细的文章,请参阅何时使用Comparable,何时使用Comparator

1
我不知道为什么没有人给这个答案点赞。它真的很好。+1 - Diganta

5

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接