java 中Comparable与Comparator详解与比较
java中Comparable与Comparator详解
今天查看TreeMap的源码,发现其键必须是实现Comparable或者Comparator的接口时产生了一些兴趣,比如在TreeMap中的put方法分别对Comparable和Comparator接口分别进行处理。那么疑问就来了,Comparable和Comparator接口的区别是什么,Java中为什么会存在两个类似的接口?
Comparable和Comparator接口都是用来比较大小的,首先来看一下Comparable的定义:
packagejava.lang; importjava.util.*; publicinterfaceComparable{ publicintcompareTo(To); }
Comparator的定义如下:
packagejava.util; publicinterfaceComparator{ intcompare(To1,To2); booleanequals(Objectobj); }
Comparable对实现它的每个类的对象进行整体排序。这个接口需要类本身去实现(这句话没看懂?没关系,接下来看个例子就明白了)。若一个类实现了Comparable接口,实现Comparable接口的类的对象的List列表(或数组)可以通过Collections.sort(或Arrays.sort)进行排序。此外,实现Comparable接口的类的对象可以用作“有序映射(如TreeMap)”中的键或“有序集合(TreeSet)”中的元素,而不需要指定比较器。
举例(类Person1实现了Comparable接口)
packagecollections; publicclassPerson1implementsComparable{ privateintage; privateStringname; publicPerson1(Stringname,intage) { this.name=name; this.age=age; } @Override publicintcompareTo(Person1o) { returnthis.age-o.age; } @Override publicStringtoString() { returnname+":"+age; } }
可以看到Person1实现了Comparable接口中的compareTo方法。实现Comparable接口必须修改自身的类,即在自身类中实现接口中相应的方法。
测试代码:
Person1person1=newPerson1("zzh",18); Person1person2=newPerson1("jj",17); Person1person3=newPerson1("qq",19); Listlist=newArrayList<>(); list.add(person1); list.add(person2); list.add(person3); System.out.println(list); Collections.sort(list); System.out.println(list);
输出结果:
[zzh:18,jj:17,qq:19] [jj:17,zzh:18,qq:19]
如果我们的这个类无法修改,譬如String,我们又要对其进行排序,当然String中已经实现了Comparable接口,如果单纯的用String举例就不太形象。对类自身无法修改这就用到了Comparator这个接口(策略模式)。
publicfinalclassPerson2 { privateintage; privateStringname; publicPerson2(Stringname,intage) { this.name=name; this.age=age; } @Override publicStringtoString() { returnname+":"+age; } //getterandsetter方法省略.... }
如类Person2,这个类已经固定,无法进行对其类自身的修改,也修饰词final了,你也别想继承再implementsComparable,那么此时怎么办呢?在类的外部使用Comparator的接口。如下测试代码:
Person2p1=newPerson2("zzh",18); Person2p2=newPerson2("jj",17); Person2p3=newPerson2("qq",19); Listlist2=newArrayList (); list2.add(p1); list2.add(p2); list2.add(p3); System.out.println(list2); Collections.sort(list2,newComparator (){ @Override publicintcompare(Person2o1,Person2o2) { if(o1==null||o2==null) return0; returno1.getAge()-o2.getAge(); } }); System.out.println(list2);
输出结果:
[zzh:18,jj:17,qq:19] [jj:17,zzh:18,qq:19]
这里(publicstatic
再譬如博主遇到的真实案例中,需要对String进行排序,且不区分大小写,我们知道String中的排序是字典排序,譬如:AaD排序之后为ADa,这样显然不对,那么该怎么办呢?同上(下面代码中的list是一个String的List集合):
Collections.sort(list,newComparator() { @Override publicintcompare(Stringo1,Stringo2) { if(o1==null||o2==null) return0; returno1.toUpperCase().compareTo(o2.toUpperCase()); } });
这样就可以实现不区分大小进行排序String的集合了,是不是很方便~
细心的同学可能会有疑问,明明在Comparator接口中定义了两个方法,为什么继承的时候只实现了一个方法,难道要颠覆我对Java接口常识的理解了嚒?
实际上,我们知道当一个类没有显式继承父类的时候,会有一个默认的父类,即java.lang.Object,在Object类中有一个方法即为equals方法,所以这里并不强制要求实现Comparator接口的类要实现equals方法,直接调用父类的即可,虽然你显式的实现了equals()方法willbeabetterchoice~
在《EffectiveJava》一书中,作者JoshuaBloch推荐大家在编写自定义类的时候尽可能的考虑实现一下Comparable接口,一旦实现了Comparable接口,它就可以跟许多泛型算法以及依赖于改接口的集合实现进行协作。你付出很小的努力就可以获得非常强大的功能。
事实上,Java平台类库中的所有值类都实现了Comparable接口。如果你正在编写一个值类,它具有非常明显的内在排序关系,比如按字母顺序、按数值顺序或者按年代顺序,那你就应该坚决考虑实现这个接口。
compareTo方法不但允许进行简单的等同性进行比较,而且语序执行顺序比较,除此之外,它与Object的equals方法具有相似的特征,它还是一个泛型。类实现了Comparable接口,就表明它的实例具有内在的排序关系,为实现Comparable接口的对象数组进行排序就这么简单:Arrays.sort(a);
对存储在集合中的Comparable对象进行搜索、计算极限值以及自动维护也同样简单。列如,下面的程序依赖于String实现了Comparable接口,它去掉了命令行参数列表中的重复参数,并按字母顺序打印出来:
publicclassWordList{ publicstaticvoidmain(Stringargs[]){ Sets=newTreeSet (); Collections.addAll(s,args); System.out.println(s); } }
Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”。而Comparator是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
前者应该比较固定,和一个具体类相绑定,而后者比较灵活,它可以被用于各个需要比较功能的类使用。可以说前者属于“静态绑定”,而后者可以“动态绑定”。
我们不难发现:Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!