“您将如何按其ID或名称对员工对象的集合进行排序”。为此,我们可以使用两个接口,即Comparator and Comparable.
这似乎是常见的面试问题之一
但我没有看到为什么我应该同时使用它们来分类员工对象
我一直在考虑comparator
完成Comparable
无法做到的事情。
我理解如果对象(被比较的实例变量)具有自然排序,那么comparable
是正确的选择。
但如果需要自定义排序(例如字符串长度),则可以编写comparator.
我的观点是comparator
只有客户才需要按照其他标准对数据进行排序。
例如,我会使用Employee class
实现id
按comparable 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);
}
}
由于
答案 0 :(得分:8)
比较器的优势是什么?
它不是“优越的”。只是两个接口以不同的方式(大致)做同样的事情。在Comparable
情况下,排序逻辑位于正在排序的对象中。在Comparator
的情况下,逻辑与声明的对象位于不同的类中。
但我没有看到为什么我应该同时使用它们来分类员工对象
如果您需要能够将对象排序为不同的顺序,那么使用两者的唯一情况就是如此。然后,您可以将相关类声明为针对“自然”订单实现Comparable
,并使用Comparator
个对象来实现其他订单。
顺便说一下,比较器可能不应该实现Comparable
,反之亦然。
如果比较器实现了Comparable
,这意味着你正在尝试对比较器对象的实例进行排序......
您的PersonComparator
课程名称错误。它应该被称为Person
。
您能否在答案中澄清一点:我们已经从Object类中获取了
equals()
方法,那么为什么Comparator
接口再次促进了equals()
方法?
一些观点:
您似乎仍然混淆了Comparable
和Comparator
的目的。 equals
对象上的Comparator
方法将比较器与其他比较器进行比较!!
equals
方法告诉您两个对象是否相等...不是哪一个是第一个。
Comparator
覆盖equals
的原因 ,以便they can clearly document equals(Object)
在{ {1}}对象。 (实际行为与Comparator
完全一致......但是他们显然认为有必要这样做,因为程序员反复得到方法的语义错误。)
答案 1 :(得分:7)
Comparable
接口 Comparable
接口定义了类型的自然顺序。假设您有一个String
或Integer
个对象的列表;你可以将该列表传递给
Collections.sort(list);
你将有一个排序列表。怎么样?由于String
和Integer
都实现了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
类,其中包含字段id
,firstName
和lastName
。如果实现Comparable
,则只能在compareTo
方法中编写一个排序逻辑。
如果实现Comparator
,则可以通过创建单独的类来创建单独的排序序列。例如IdSorter
,FirstNameSorter
和LastNameSorter
为您提供了以多种方式对Employee
进行排序的方法。
阅读
答案 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)
答案 6 :(得分:0)
如果您正在使用比较器,您只需添加一个比较器类并将其与List对象一起传递给Collections.sort()方法,而不会对现有代码进行其他更改。
但如果实现类似的接口,则必须更改所有模型/ bean类的代码以覆盖compareTo()方法。
所以松散耦合比较器更好。