对比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