对比Java中的Comparable排序接口和Comparator比较器接口
Comparable
Comparable是排序接口。
若一个类实现了Comparable接口,就意味着“该类支持排序”。即然实现Comparable接口的类支持排序,假设现在存在“实现Comparable接口的类的对象的List列表(或数组)”,则该List列表(或数组)可以通过Collections.sort(或Arrays.sort)进行排序。
此外,“实现Comparable接口的类的对象”可以用作“有序映射(如TreeMap)”中的键或“有序集合(TreeSet)”中的元素,而不需要指定比较器。
Comparable接口仅仅只包括一个函数,它的定义如下:
packagejava.lang;
importjava.util.*;
publicinterfaceComparable<T>{
publicintcompareTo(To);
}
说明:假设我们通过x.compareTo(y)来“比较x和y的大小”。若返回“负数”,意味着“x比y小”;返回“零”,意味着“x等于y”;返回“正数”,意味着“x大于y”。
Comparable接口已经泛型化了,所以实现Comparable的对象声明它可以与什么类型进行比较。(通常,这是对象本身的类型,但是有时也可能是父类。)
publicinterfaceComparable{publicbooleancompareTo(Tother);}
所以Comparable接口包含一个类型参数T,该参数是一个实现Comparable的类可以与之比较的对象的类型。这意味着如果定义一个实现Comparable的类,比如String,就必须不仅声明类支持比较,还要声明它可与什么比较(通常是与它本身比较):
publicclassStringimplementsComparable{...}
现在来考虑一个二元max()方法的实现。您想要接受两个相同类型的参数,二者都是Comparable,并且相互之间是Comparable。幸运的是,如果使用泛型方法和有限制类型参数的话,这相当直观:
publicstatic>Tmax(Tt1,Tt2){if(t1.compareTo(t2)>0)returnt1;elsereturnt2;}
在本例中,您定义了一个泛型方法,在类型T上泛型化,您约束该类型扩展(实现)Comparable。两个参数都必须是T类型,这表示它们是相同类型,支持比较,并且相互可比较。容易!
更好的是,编译器将使用类型推理来确定当调用max()时T的值表示什么意思。所以根本不用指定T,下面的调用就能工作:
Strings=max("moo","bark");
编译器将计算出T的预定值是String,因此它将进行编译和类型检查。但是如果您试图用不实现Comparable的类X的参数调用max(),那么编译器将不允许这样做。
Comparator
Comparator是比较器接口。
我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);那么,我们可以建立一个“该类的比较器”来进行排序。这个“比较器”只需要实现Comparator接口即可。
也就是说,我们可以通过“实现Comparator类来新建一个比较器”,然后通过该比较器对类进行排序。
Comparator接口仅仅只包括两个个函数,它的定义如下:
packagejava.util;
publicinterfaceComparator<T>{
intcompare(To1,To2);
booleanequals(Objectobj);
}
说明:
1.若一个类要实现Comparator接口:它一定要实现compareTo(To1,To2)函数,但可以不实现equals(Objectobj)函数。
为什么可以不实现equals(Objectobj)函数呢?因为任何类,默认都是已经实现了equals(Objectobj)的。Java中的一切类都是继承于java.lang.Object,在Object.java中实现了equals(Objectobj)函数;所以,其它所有的类也相当于都实现了该函数。
2.intcompare(To1,To2)是“比较o1和o2的大小”。返回“负数”,意味着“o1比o2小”;返回“零”,意味着“o1等于o2”;返回“正数”,意味着“o1大于o2”。
Comparator和Comparable比较
Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”。
而Comparator是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
我们不难发现:Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
我们通过一个测试程序来对这两个接口进行说明。源码如下:
importjava.util.*;
importjava.lang.Comparable;
/**
*@desc"Comparator"和“Comparable”的比较程序。
*(01)"Comparable"
*它是一个排序接口,只包含一个函数compareTo()。
*一个类实现了Comparable接口,就意味着“该类本身支持排序”,它可以直接通过Arrays.sort()或Collections.sort()进行排序。
*(02)"Comparator"
*它是一个比较器接口,包括两个函数:compare()和equals()。
*一个类实现了Comparator接口,那么它就是一个“比较器”。其它的类,可以根据该比较器去排序。
*
*综上所述:Comparable是内部比较器,而Comparator是外部比较器。
*一个类本身实现了Comparable比较器,就意味着它本身支持排序;若它本身没实现Comparable,也可以通过外部比较器Comparator进行排序。
*/
publicclassCompareComparatorAndComparableTest{
publicstaticvoidmain(String[]args){
//新建ArrayList(动态数组)
ArrayList<Person>list=newArrayList<Person>();
//添加对象到ArrayList中
list.add(newPerson("ccc",20));
list.add(newPerson("AAA",30));
list.add(newPerson("bbb",10));
list.add(newPerson("ddd",40));
//打印list的原始序列
System.out.printf("Originalsort,list:%s\n",list);
//对list进行排序
//这里会根据“Person实现的Comparable<String>接口”进行排序,即会根据“name”进行排序
Collections.sort(list);
System.out.printf("Namesort,list:%s\n",list);
//通过“比较器(AscAgeComparator)”,对list进行排序
//AscAgeComparator的排序方式是:根据“age”的升序排序
Collections.sort(list,newAscAgeComparator());
System.out.printf("Asc(age)sort,list:%s\n",list);
//通过“比较器(DescAgeComparator)”,对list进行排序
//DescAgeComparator的排序方式是:根据“age”的降序排序
Collections.sort(list,newDescAgeComparator());
System.out.printf("Desc(age)sort,list:%s\n",list);
//判断两个person是否相等
testEquals();
}
/**
*@desc测试两个Person比较是否相等。
*由于Person实现了equals()函数:若两person的age、name都相等,则认为这两个person相等。
*所以,这里的p1和p2相等。
*
*TODO:若去掉Person中的equals()函数,则p1不等于p2
*/
privatestaticvoidtestEquals(){
Personp1=newPerson("eee",100);
Personp2=newPerson("eee",100);
if(p1.equals(p2)){
System.out.printf("%sEQUAL%s\n",p1,p2);
}else{
System.out.printf("%sNOTEQUAL%s\n",p1,p2);
}
}
/**
*@descPerson类。
*Person实现了Comparable接口,这意味着Person本身支持排序
*/
privatestaticclassPersonimplementsComparable<Person>{
intage;
Stringname;
publicPerson(Stringname,intage){
this.name=name;
this.age=age;
}
publicStringgetName(){
returnname;
}
publicintgetAge(){
returnage;
}
publicStringtoString(){
returnname+"-"+age;
}
/**
*比较两个Person是否相等:若它们的name和age都相等,则认为它们相等
*/
booleanequals(Personperson){
if(this.age==person.age&&this.name==person.name)
returntrue;
returnfalse;
}
/**
*@desc实现“Comparable<String>”的接口,即重写compareTo<Tt>函数。
*这里是通过“person的名字”进行比较的
*/
@Override
publicintcompareTo(Personperson){
returnname.compareTo(person.name);
//returnthis.name-person.name;
}
}
/**
*@descAscAgeComparator比较器
*它是“Person的age的升序比较器”
*/
privatestaticclassAscAgeComparatorimplementsComparator<Person>{
@Override
publicintcompare(Personp1,Personp2){
returnp1.getAge()-p2.getAge();
}
}
/**
*@descDescAgeComparator比较器
*它是“Person的age的升序比较器”
*/
privatestaticclassDescAgeComparatorimplementsComparator<Person>{
@Override
publicintcompare(Personp1,Personp2){
returnp2.getAge()-p1.getAge();
}
}
}
下面对这个程序进行说明。
1.Person类定义。如下:
privatestaticclassPersonimplementsComparable<Person>{
intage;
Stringname;
...
/**
*@desc实现“Comparable<String>”的接口,即重写compareTo<Tt>函数。
*这里是通过“person的名字”进行比较的
*/
@Override
publicintcompareTo(Personperson){
returnname.compareTo(person.name);
//returnthis.name-person.name;
}
}
说明:
(1)Person类代表一个人,Persong类中有两个属性:age(年纪)和name“人名”。
(2)Person类实现了Comparable接口,因此它能被排序。
2.在main()中,我们创建了Person的List数组(list)。如下:
//新建ArrayList(动态数组)
ArrayList<Person>list=newArrayList<Person>();
//添加对象到ArrayList中
list.add(newPerson("ccc",20));
list.add(newPerson("AAA",30));
list.add(newPerson("bbb",10));
list.add(newPerson("ddd",40));
3.接着,我们打印出list的全部元素。如下:
//打印list的原始序列
System.out.printf("Originalsort,list:%s\n",list);
4.然后,我们通过Collections的sort()函数,对list进行排序。
由于Person实现了Comparable接口,因此通过sort()排序时,会根据Person支持的排序方式,即compareTo(Personperson)所定义的规则进行排序。如下:
//对list进行排序
//这里会根据“Person实现的Comparable<String>接口”进行排序,即会根据“name”进行排序
Collections.sort(list);
System.out.printf("Namesort,list:%s\n",list);
5.对比Comparable和Comparator
我们定义了两个比较器AscAgeComparator和DescAgeComparator,来分别对Person进行升序和降低排序。
6.AscAgeComparator比较器
它是将Person按照age进行升序排序。代码如下:
/**
*@descAscAgeComparator比较器
*它是“Person的age的升序比较器”
*/
privatestaticclassAscAgeComparatorimplementsComparator<Person>{
@Override
publicintcompare(Personp1,Personp2){
returnp1.getAge()-p2.getAge();
}
}
7.DescAgeComparator比较器
它是将Person按照age进行降序排序。代码如下:
/**
*@descDescAgeComparator比较器
*它是“Person的age的升序比较器”
*/
privatestaticclassDescAgeComparatorimplementsComparator<Person>{
@Override
publicintcompare(Personp1,Personp2){
returnp2.getAge()-p1.getAge();
}
}
8.运行结果运行程序,输出如下:
Originalsort,list:[ccc-20,AAA-30,bbb-10,ddd-40] Namesort,list:[AAA-30,bbb-10,ccc-20,ddd-40] Asc(age)sort,list:[bbb-10,ccc-20,AAA-30,ddd-40] Desc(age)sort,list:[ddd-40,AAA-30,ccc-20,bbb-10] eee-100EQUALeee-100