何时使用Comparable和Comparator

时间:2010-02-15 15:12:54

标签: java comparator comparable

我有一个对象列表,我需要对一个字段进行排序,比如分数。在没有多想的情况下,我编写了一个实现Comparator的新类,它可以执行任务并且可以正常运行。

现在回过头来看,我想知道我是否应该让我的类实现Comparable,而不是创建一个实现Comparator的新类。分数是对象将被订购的唯一字段。

  1. 我做了什么可以接受的做法?

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

  3. 如果上面的(2)为真,那么它是否意味着只有在具有类实现Comparable之后才应该实现Comparator? (假设我拥有原始课程。)

19 个答案:

答案 0 :(得分:121)

如果要定义相关对象的默认(自然)排序行为,请使用Comparable,通常的做法是使用技术或自然(数据库?)标识符对象。

如果要定义外部可控排序行为,请使用Comparator,这可以覆盖默认排序行为。

答案 1 :(得分:76)

我想说一个对象应该实现Comparable,如果这是对类进行排序的明确的自然方式,并且任何人都需要对类进行排序通常会想要这样做。

但是,如果排序是一个不寻常的类使用,或者排序只对特定用例有意义,那么比较器是一个更好的选择。

换句话说,鉴于班级名称,是否清楚了类似的排序方式,还是你必须求助于阅读javadoc?如果是后者,可能性是每个未来的排序用例都需要一个比较器,此时可比较的实现可能会减慢类的用户速度,而不是加速它们。

答案 2 :(得分:53)

使用Comparable

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

使用Comparator

  • 如果对象超出您的控制范围,您无法实现Comparable
  • 当您想要比较与默认行为(由Comparable指定)行为不同的行为时。

答案 3 :(得分:20)

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

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

  • 能够将当前对象与提供的对象进行比较。
  • 通过使用此功能,我们可以根据实例属性实施 only one sort sequence 。 EX:Person.id
  • 某些预定义类(如String,Wrapper类,Date,Calendar)已实现Comparable接口。

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

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

  • 能够比较任何两个相同类型的对象。
  • 通过使用此功能,我们可以实现 many sort sequence ,并根据实例属性为每个名称命名。 EX: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,请参阅我的帖子。

答案 4 :(得分:9)

比较同一类的实例时,应使用Comparable。

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

Comparable由类实现,需要为其对象定义自然顺序。 像String实现Comparable。

如果一个人想要一个不同的排序顺序,那么他可以实现比较器并定义自己比较两个实例的方式。

答案 5 :(得分:9)

比较者做了可比较的事情,还有更多。

  

| | Comparable | Comparator ._______________________________________________________________________________ Is used to allow Collections.sort to work | yes | yes Can compare multiple fields | yes | yes Lives inside the class you’re comparing and serves | | as a “default” way to compare | yes | yes Can live outside the class you’re comparing | no | yes Can have multiple instances with different method names | no | yes Input arguments can be a list of | just Object| Any type Can use enums | no | yes

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

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

    等...

现在,您可以在任何地方使用这些排序方法并重用代码。 这给了我一切可比的东西,加上更多...所以我认为没有任何理由可以使用可比较的。

答案 6 :(得分:8)

我会说:

  • 如果比较直观,那么一定要实现Comparable
  • 如果不清楚您的比较是否直观,请使用比较器,因为它更多 对于那些必须维护代码的可怜的灵魂来说,显而易见,因此更加明确
  • 如果有多个直观的比较可能我更喜欢比较器, 可能是由要比较的类中的工厂方法构建的。
  • 如果比较是特殊目的,请使用Comparator

答案 7 :(得分:4)

  • 如果在写课时 你只有一个排序用例 使用可比较。
  • 只有当你有一个以上的时候 分拣策略 比较器。

答案 8 :(得分:4)

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

Comparable和Comparator之间的主要区别:

AcNumber

答案 9 :(得分:4)

以下几点可帮助您确定应使用Comparable的哪种情况以及哪种Comparator:

1)Code Availabilty

2)单一与多重排序标准

3)Arays.sort()和Collection.sort()

4)作为SortedMap和SortedSet中的键

5)更多课程数量与灵活性

6)班级间比较

7)自然秩序

有关更详细的文章,请参阅When to use comparable and when to use comparator

答案 10 :(得分:3)

如果您需要自然顺序排序 - 用户可比较 如果您需要自定义订单排序 - 使用比较器

示例:

Class Employee{
private int id;
private String name;
private String department;
}

自然顺序排序将基于id,因为它将是唯一的,并且自定义顺序排序将是名称和部门。

Refrences:
When should a class be Comparable and/or Comparator? http://javarevisited.blogspot.com/2011/06/comparator-and-comparable-in-java.html

答案 11 :(得分:3)

这里有一个类似的问题:When should a class be Comparable and/or Comparator?

我会说以下内容: 实现可比较的东西,如自然排序,例如基于内部ID

如果您有更复杂的比较算法,请执行比较器,例如多个领域等等。

答案 12 :(得分:2)

<强>比较的:
 每当我们只想存储同类元素和默认的自然排序顺序时,我们就可以去实现comparable接口的类。

<强>比较
每当我们想要存储同构和异构元素并且我们想要按默认的自定义排序顺序排序时,我们就可以选择comparator接口。

答案 13 :(得分:0)

如果您拥有该课程,请选择可比较。一般情况下,如果您不拥有该类,则使用比较器,但您必须使用 TreeSet TreeMap ,因为Comparator可以作为参数传递TreeSet或TreeMap的结构。您可以在http://preciselyconcise.com/java/collections/g_comparator.php

中查看如何使用Comparator和Comparable

答案 14 :(得分:0)

我的需求基于日期排序。

所以,我使用了Comparable,它很适合我。

public int compareTo(GoogleCalendarBean o) {
    // TODO Auto-generated method stub
    return eventdate.compareTo(o.getEventdate());
}

Comparable的一个限制是它们不能用于List以外的集合。

答案 15 :(得分:0)

在一次采访中,我被要求在nlogn时间内对一定数量的数字进行排序。 (不使用计数排序)

在对象上实现Comparable接口允许隐式排序算法使用重写的compareTo方法对排序元素进行排序,这将是线性时间。

答案 16 :(得分:0)

Comparable是为数字值升序提供的默认自然排序顺序,为字符串是字母顺序提供的默认自然排序顺序。 例如:

Treeset t=new Treeset();
t.add(2);
t.add(1);
System.out.println(t);//[1,2]

Comparator是在自定义myComparator类中通过覆盖compare方法实现的自定义排序顺序 例如:

Treeset t=new Treeset(new myComparator());
t.add(55);
t.add(56);
class myComparator implements Comparator{
public int compare(Object o1,Object o2){
//Descending Logic
}
}
System.out.println(t);//[56,55]

答案 17 :(得分:-1)

非常简单的方法是假设有问题的实体类在数据库中表示,然后在数据库表中你需要由实体类的字段组成的索引吗?如果答案为是,则实施可比较并使用索引字段进行自然排序。在所有其他情况下使用比较器。

答案 18 :(得分:-2)

我的注释库,用于实现ComparableComparator

public class Person implements Comparable<Person> {         
    private String firstName;  
    private String lastName;         
    private int age;         
    private char gentle;         

    @Override         
    @CompaProperties({ @CompaProperty(property = "lastName"),              
        @CompaProperty(property = "age",  order = Order.DSC) })           
    public int compareTo(Person person) {                 
        return Compamatic.doComparasion(this, person);         
    }  
}

单击链接以查看更多示例。 http://code.google.com/p/compamatic/wiki/CompamaticByExamples