何时使用可比和比较器


108

我有一个需要在字段上排序的对象列表,例如“分数”。我不加思索地编写了一个实现Comparator的新类,该类可以完成任务并且可以工作。

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

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

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

  3. 如果上面的(2)是正确的,那是否意味着只有在类实现Comparable之后才应该实现Comparator?(假设我拥有原始班级)。

Answers:


80

我要说的是,如果对象是对类进行排序的明显自然方法,则该对象应实现Comparable,并且任何需要对类进行排序的人通常都希望采用这种方式。

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

换句话说,给定类名,是否清楚可比对象如何排序,还是必须诉诸于Javadoc?如果是后者,则很可能每个将来的排序用例都需要一个比较器,这时可比的实现可能会减慢该类的用户,而不是加快它们的速度。


你能举个简单的例子吗?
rgamber

这可能是一个很好的例子:gist.github.com/yclian/2627608有使用ComparableVersion的Version类。版本-提供带有工厂方法的ComparableVersion应该是对象(没有静态方法)-提供可以与另一个版本进行比较的版本。责任是分开的。
ses


采访者问,为什么可以使用Comparable进行比较,而我却傻了:(
Aadam

@aLearner链接已失效
G.Brown

127

使用Comparable,如果要定义一个默认的(天然)排序问题的对象的行为,常见的做法是使用的技术或自然(数据库?)的对象标识符这一点。

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


3
这是一个技术性的解释,尽管它们是正确的,但实际上并没有说明最佳实践。
Extraneon 2010年

40
它告诉何时使用每种方法-如果这不是最佳做法,那是什么?
博佐2010年

1
“实施Comparable意味着我要定义自然秩序吗?” ,这给了我我想要的答案。谢谢:)
Somjit 2013年

61

用途Comparable

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

用途Comparator

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

20

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

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

  • 能够将当前对象与提供的对象进行比较。
  • 通过使用它,我们可以only one sort sequence基于实例属性来实现。例如:Person.id
  • 一些预定义的类(如String,包装类,日期,日历)已实现Comparable接口。

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

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

  • 能够比较任何两个相同类型的对象。
  • 通过使用它,我们可以many sort sequence根据实例属性实现和命名每个。例如: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:比较器,请参阅我的文章。


10

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

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

可比较由类实现,该类需要为其对象定义自然顺序。像String一样实现Comparable。

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


10

如果对象的排序需要基于自然顺序,则请使用Comparable,而如果需要对不同对象的属性进行排序,请使用Java中的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.        ¦
+------------------------------------------------------------------------------------+

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

    等等...

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


8

我会说:

  • 如果比较是直观的,那么一定要实现可比性
  • 如果不清楚您的比较是否直观,请使用“比较器”,因为它更明确,因此对于必须维护代码的可怜灵魂而言更清晰
  • 如果可能有多个直观比较,则我希望使用Comparator,可能是通过要比较的类中的工厂方法构建的。
  • 如果比较是出于特殊目的,请使用比较器

6

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

1)代码可用性

2)单项与多项排序标准

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

4)作为SortedMap和SortedSet中的键

5)更多的班级数量和灵活性

6)类间比较

7)自然秩序

有关更详细的文章,您可以参考何时使用可比性和何时使用比较器


我不知道为什么没有人赞成这个答案。这是一个非常好的。+1
Diganta

4
  • 如果在编写该类时,您只有一个使用排序的用例,则使用Comparable。
  • 仅当您有多种分类策略时,才应使用比较器。



2

可比性:
每当我们只想存储同质元素和所需的默认自然排序顺序时,我们都可以使用类实现 comparable接口。

比较器:
每当我们要存储同质和异构元素并且要按默认的自定义排序顺序进行排序时,我们都可以使用comparator接口。


0

我的需要是根据日期排序。

因此,我使用了Comparable,它对我来说很容易工作。

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

可比性的一个限制是它们不能用于列表以外的集合。



0

在一次采访中,我被问到比nlogn时间更好地对一定范围的数字进行排序。(不使用计数排序)

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


0

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

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

比较器是在自定义myComparator类中通过覆盖以下比较方法实现的自定义排序顺序:

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]

-1

非常简单的方法是假设所讨论的实体类在数据库中表示,然后在数据库表中您是否需要由实体类的字段组成的索引?如果答案是肯定的,则实施可比性并将索引字段用于自然排序顺序。在所有其他情况下,请使用比较器。


-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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.