6

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

但是我看不出为什么我应该同时使用这两种方法来对员工对象进行排序

我一直在思考什么comparatorComparable做不到的。我知道如果对象(比较的实例变量)具有自然顺序,那么comparable是正确的选择。但是如果需要自定义排序(例如字符串长度),那么可以comparator. 在这里写一个我的观点,comparator只有当客户想按照其他标准对数据进行排序时才需要。例如,我将实现一个Employee class使用. 但如果客户想按(名称)对 Employee 对象进行排序,他将实现为具体类或匿名排序。我在这里有什么遗漏吗?idcomparable interfaceStringcomparator

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

最后,与其他方法相比,以下其中一项是否有任何额外的好处我以两种方式实现了比较器 1. 作为被注释掉的静态方法 2. 作为被注释掉的主要方法中的匿名对象(?) 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);
 }

}

谢谢

4

7 回答 7

9

Comparable界面

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

Collections.sort(list);

你将有一个排序列表。如何?因为实现接口和接口的实现都String提供了自然排序。就像类定义说的那样——“如果你找到我类型的对象集合,请根据我在方法中定义的策略对它们进行排序”。IntegerComparableComparablecompareTo

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

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());
    }

}

在这里,StrategyOnePerson根据对象名称的长度对对象进行排序,StrategyTwo并将Person根据对象名称的字典顺序对对象进行排序。

实施方式Comparator

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

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接口用于定义对象排序的特定策略。

于 2013-08-31T06:03:33.537 回答
8

比较器在哪些方面优于可比较器?

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

但是我看不出为什么我应该同时使用这两种方法来对员工对象进行排序

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

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

如果比较器实现Comparable这意味着您正在尝试订购比较器对象本身的实例...

您的PersonComparator班级名称错误。真的应该叫它Person


您能否在回答中澄清一件事,即我们已经equals()从 Object 类中获得了方法,那么为什么Comparator接口再次促进该equals()方法?

几点:

  • 您似乎仍然对Comparableand的目的感到困惑Comparator。对象上的equals方法Comparator将比较器与其他比较器进行比较!!

  • equals方法告诉您两个对象是否相等......而不是哪个先出现。

  • Comparator覆盖equals的原因仅仅是为了让他们可以清楚地记录您在对象equals(Object)上调用它时所做的事情。Comparator(实际行为与Object.equals(Object)……完全一致,但他们显然认为有必要这样做,因为程序员反复弄错方法的语义。)

于 2013-08-31T04:08:11.650 回答
4

比较器在哪些方面优于可比较器?

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

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

考虑Employee具有字段id和的firstNamelastName。如果你实现Comparable,你可以在方法中只写一个排序逻辑compareTo

如果您实现Comparator,那么您可以通过创建单独的类来创建单独的排序序列。例如IdSorterFirstNameSorterLastNameSorter使您能够以多种方式进行排序Employee

使用 Comparator 对用户定义的对象进行排序

于 2013-08-31T04:33:26.023 回答
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);
    }
}

在上述 Comparable 的情况下,您可以使用 age 或 name 对项目进行排序。但在 Comparator 的情况下,您可以根据多个字段对项目进行排序。

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 的优点是可以灵活地根据对象的多个字段对列表进行排序。

于 2013-08-31T07:39:13.450 回答
0

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

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

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

于 2013-08-31T04:31:16.497 回答
0

给你……我已经在图片和解释的帮助下写了很多关于这个澄清的文章。

请找到以下链接:

可比和比较器

有人认为您可以始终记住,那就是“它们不能互换使用”

于 2013-08-31T04:37:31.567 回答
0

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

但是如果您实现可比较的接口,您将不得不更改所有模型/bean 类的代码以覆盖 compareTo() 方法。

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

于 2016-10-24T13:17:46.560 回答