比较优越的比较器是什么?

时间:2013-08-31 01:36:09

标签: java sorting comparator comparable

“您将如何按其ID或名称对员工对象的集合进行排序”。为此,我们可以使用两个接口,即Comparator and Comparable. 这似乎是常见的面试问题之一

但我没有看到为什么我应该同时使用它们来分类员工对象

我一直在考虑comparator完成Comparable无法做到的事情。 我理解如果对象(被比较的实例变量)具有自然排序,那么comparable是正确的选择。 但如果需要自定义排序(例如字符串长度),则可以编写comparator. 我的观点是comparator只有客户才需要按照其他标准对数据进行排序。 例如,我会使用Employee class实现idcomparable interface排序。 但是如果客户端想要按String(名称)对Employee对象进行排序,他会将comparator实现为具体类或匿名排序。 这里有什么我想念的吗?

例如,在下面的代码中,对于Person对象,我的compareTo方法比较年龄并对其进行排序 在compare方法中,我使用String length(人名)进行排序。从理论上讲,我可以在compareTo方法中完成这两个方法,如下所示。

最后,以下其中一项优于其他优势 我已经用两种方式实现了比较器 1.作为一种被注释掉的静态方法 2.作为注释掉的main方法中的匿名对象(?) 3.创建一个实现比较器的新类,并在collections.sort()中调用该类的实例 - 这个我没有在这里完成

(The commented-out parts of the code works. They are just different implementations)

mport java.util.Collections;
import java.util.Comparator;
import java.util.*;

public class PersonComparator implements Comparable{
    private String name;
    private int age;

    public PersonComparator(String name, int age) {
        this.name = name;
        this.age = age;
    }
@Override
public String toString() {
    return "name=" + name + ", age=" + age;
}

/*@Override
public int compareTo(Object obj) {
    if (!(obj instanceof PersonComparator)) {
        throw new ClassCastException("Invalid object");
    }
    PersonComparator p2 = (PersonComparator)obj;
    return this.age-p2.age;
}*/

/*Alternative CompareTo that checks for both age and name*/
 public int compareTo(Object obj) {
    if (!(obj instanceof PersonComparator)) {
        throw new ClassCastException("Invalid object");
    }
    PersonComparator p2 = (PersonComparator)obj;
    if (this.age!=p2.age){
        return this.age-p2.age;
    }
    else {
    return (this.name.length()-p2.name.length());
}
} 


/*public static Comparator nameLengthComparator 
= new Comparator() {


    @Override
    public int compare(Object obj1, Object obj2) {
        if (!(obj1 instanceof PersonComparator) || !(obj2 instanceof PersonComparator)){
            throw new ClassCastException("Invalid object");
        }
        else {
            PersonComparator p1 = (PersonComparator)obj1;
            PersonComparator p2 = (PersonComparator)obj2;
            return p1.name.length()-p2.name.length();
        }
}
};*/

 public static void main(String[] args){
     PersonComparator p1 = new PersonComparator("Alexander", 45);
     PersonComparator p2 = new PersonComparator("Pat", 27);
     PersonComparator p3 = new PersonComparator("Zacky", 45);
     PersonComparator p4 = new PersonComparator("Rake", 34);

     List<PersonComparator> list = new ArrayList<PersonComparator>();
     list.add(p1);
     list.add(p2);
     list.add(p3);
     list.add(p4);

     System.out.println("Before sorting "+ list);
     Collections.sort(list);
     //System.out.println("After sorting by age "+ list);
     //System.out.println("Before sorting "+ list);
     //Collections.sort(list, nameLengthComparator);
     System.out.println("After sorting by name length "+ list);
     /*Collections.sort(list, new Comparator<PersonComparator>() {
         @Override
            public int compare(PersonComparator p1, PersonComparator p2) {
                    return p1.name.length()-p2.name.length();
                }
        }
    );*/
     System.out.println("After sorting by name length "+ list);
 }

}

由于

7 个答案:

答案 0 :(得分:8)

  

比较器的优势是什么?

它不是“优越的”。只是两个接口以不同的方式(大致)做同样的事情。在Comparable情况下,排序逻辑位于正在排序的对象中。在Comparator的情况下,逻辑与声明的对象位于不同的类中。

  

但我没有看到为什么我应该同时使用它们来分类员工对象

如果您需要能够将对象排序为不同的顺序,那么使用两者的唯一情况就是如此。然后,您可以将相关类声明为针对“自然”订单实现Comparable,并使用Comparator个对象来实现其他订单。

顺便说一下,比较器可能不应该实现Comparable,反之亦然。

如果比较器实现了Comparable,这意味着你正在尝试对比较器对象的实例进行排序......

您的PersonComparator课程名称错误。它应该被称为Person


  

您能否在答案中澄清一点:我们已经从Object类中获取了equals()方法,那么为什么Comparator接口再次促进了equals()方法?

一些观点:

  • 您似乎仍然混淆了ComparableComparator的目的。 equals对象上的Comparator方法将比较器与其他比较器进行比较!!

  • equals方法告诉您两个对象是否相等...不是哪一个是第一个。

  • Comparator覆盖equals的原因 ,以便they can clearly document equals(Object)在{ {1}}对象。 (实际行为与Comparator完全一致......但是他们显然认为有必要这样做,因为程序员反复得到方法的语义错误。)

答案 1 :(得分:7)

Comparable接口

Comparable接口定义了类型的自然顺序。假设您有一个StringInteger个对象的列表;你可以将该列表传递给

Collections.sort(list);

你将有一个排序列表。怎么样?由于StringInteger都实现了Comparable接口,Comparable接口的实现提供了自然排序。它类似于类定义 - “如果您找到我的类型的对象集合,请根据我在compareTo方法中定义的策略对它们进行排序。

现在,当您定义自己的类型时,可以通过实现Comparable接口来定义类的对象的自然顺序。有关对象排序的更多信息,请See the Java documentation

Comparator接口

Comparator接口描述了如何为对象排序定义自定义策略。假设我们有一个简单的Person类型,如下所示:

public class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

现在,通过实施Comparator界面,您可以编写不同的策略来订购Person类型的实例。例如,考虑下面给出的Person对象排序的两种策略:

class StrategyOne implements Comparator<Person> {

    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().length() - p2.getName().length();
    }

}

class StrategyTwo implements Comparator<Person> {

    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().compareTo(p2.getName());
    }

}

此处,StrategyOne将根据名称的长度对Person个对象进行排序,StrategyTwo将根据其姓名的字典顺序排序Person个对象。

实施Comparator

的方法

如您所见,具体策略类是无状态,因此所有实例在功能上都是等效的。因此,我们只需要任何具体策略类的单个实例。因此,它应该是一个单身人士。每次执行调用时,使用匿名类将创建一个新实例。 考虑将对象存储在私有静态最终字段中,并使用静态工厂方法重新访问它。例如,您可以重复使用以下两种具体策略:

class Strategies {
    private static final Comparator<Person> PERSON_NAME_LENGTH_COMPARATOR = new StrategyOne();

    private static final Comparator<Person> PERSON_NAME_LEXICAL_COMPARATOR = new StrategyTwo();

    public static Comparator<Person> personNameLengthComparator(){
         return  PERSON_NAME_LENGTH_COMPARATOR;
    }


    public static Comparator<Person> personNameLexicalComparator(){
         return  PERSON_NAME_LEXICAL_COMPARATOR;
    }
}

摘要

总而言之,Comparable接口用于定义类的自然顺序,Comparator接口用于定义对象排序的特定策略。

答案 2 :(得分:4)

  

比较器的优势是什么?

我不会说这是优越的,但一个优点是它使我们能够编写多个排序序列。在Comparable的情况下,您必须通过您想要排序的类来实现该接口,并且您只能编写一个排序序列。

使用Comparator,您可以为排序顺序创建不同的类,在排序时,只需将Comparator实例传递给COllections.sort()方法。

考虑Employee类,其中包含字段idfirstNamelastName。如果实现Comparable,则只能在compareTo方法中编写一个排序逻辑。

如果实现Comparator,则可以通过创建单独的类来创建单独的排序序列。例如IdSorterFirstNameSorterLastNameSorter为您提供了以多种方式对Employee进行排序的方法。

阅读

Sorting user defined objects with Comparator

答案 3 :(得分:3)

Comparable允许您仅基于一个字段对集合中的项目进行排序.Comparator提供了基于多个字段比较项目的灵活性

例如。

class Person implements Comparable
{

int age;
String name;


Person(int age,String name)
{
   this.age=age;
   this.name=name;
}

public int compareTo(Object o1)   // Either you can compare according to age or name
{
    Person p = (Person)o1;
    if (this.age==p.age)
    return 0;
    else if (this.age>p.age)
    return 1;
    else
    return -1;
}


public int compareTo(Object o)    //Based on name comparision
{
     return (this.name.compareTo((Person)o).name));
}
public static void main (String args[])
{
     List<Person> list = new ArrayList<Person>();
     Person o = new Person(12,"Steve");
     Person o1 = new Person(13,"Jason");
     list.add(o);
     list.add(o1);
     Collections.sort(list);
    }
}

如果是上面的可比较,您可以使用年龄或名称对项目进行排序。但是在比较器的情况下,您可以根据多个字段对项目进行排序。

class AgeComparison implements Comparator
{
   public int compare(Object o1,Object o2)
{
        Person s1 = (Person)o1;
        Person s2 =(Person)o2;
        if (s1.age==s2.age)
        return 0;
        if(s1.age>s2.age)
        return 1;
        else
        return -1;
}

class NameComparison implements Comparator
{
     public int compare(Object o1,Object o2)
    {
        Person s1 = (Person)o1;
        Person s2 =(Person)o2;
        return (s1.age.compareTo(s2.age));
    }

}

要使用Comparator,您必须传递列表以及您必须使用的类实例。

Collections.sort(list,new NameComparison());
Collections.sort(list,new AgeComparison());

简而言之,Comparator的优点是可以灵活地根据对象的多个字段对列表进行排序。

答案 4 :(得分:0)

通常,在排序“明显”时使用Comparable。例如,对于使用字母顺序的字符串,对于使用数字顺序的数字。请注意,Comparable对象只能实现单个compareTo()方法,因此您只能获得一个选项 - “自然”,“明显”选项。优点是它很简单,客户端代码不需要做任何额外的工作来比较事物。

如果排序不太明显,或者您可能希望有多个选项,请使用Comparator。例如,书籍可能按标题,作者,ISBN等排序。您可以使用三种不同的比较器来处理这三种情况。您可能希望按一些不寻常的顺序对字符串进行排序,例如一个外语的特殊情况,忽略大写等。

此外,如果您正在排序的对象没有实现Comparable,或者您正在混合不喜欢相互比较的类型(通常,这是要避免的,但也许您希望能够比较Books和作者在一个特殊情况下的单个列表中)你需要使用比较器。

答案 5 :(得分:0)

你走了......我已经在图片和解释的帮助下写了很多关于这个澄清的内容。

请找到以下链接:

Comparable and Comparator

有人认为你永远记得,那就是“它们不能互换使用”

答案 6 :(得分:0)

如果您正在使用比较器,您只需添加一个比较器类并将其与List对象一起传递给Collections.sort()方法,而不会对现有代码进行其他更改。

但如果实现类似的接口,则必须更改所有模型/ bean类的代码以覆盖compareTo()方法。

所以松散耦合比较器更好。