Comparable<T>和Comparator<T>两个泛型接口的功能都是比较器,可以用于比较和集合排序。泛型使两个接口具有普适性,意味着可以针对不同类实现两个接口以获得比较功能。
Comparable接口定义在java.lang.Comparable中,接口中只包括compareTo一个函数。根据规约,如果调用compareTo的对象小于作为参数的对象,函数返回负整数;相等则返回0;大于则返回一个正整数。
compareTo函数的规约中强烈建议如果一个class实现了Comparable接口,那么应该有(x.compareTo(y) == 0) == (x.equals(y))。
Comparable接口:
编写一个类时可以通过实现Comparable接口将比较代码嵌入自身类中,让类自身具有比较功能,即支持自比较。在String等基本类型的Java封装类中都实现了Comparable接口,支持自比较。
Java11库中已经实现的Comparable接口:
String类中实现的compareTo:
Comparator接口在java.util.Comparator中,实现Comparator接口的类具有比较器的功能。Comparator接口中包括很多与比较相关的函数,这里主要关注其中的compare函数。
Comparator接口中对compare方法的定义:
compare函数规定,当x<y时返回一个负整数,x=y时返回0,x>y时返回正整数。同样地,规约中也建议两个对象x,y应满足(compare(x,
y)== 0) == (x.equals(y))。
Compartor接口一般在一个独立的类中实现,这个类的对象作为比较器使用。Comparator体现了策略模式:针对同一类,不同的Comparator实现可以提供不同的比较方法。可以通过委派,在运行时动态传入需要使用的比较方法。
Comparable可以被认为是一个内比较器,在实现Comparable接口的ADT内部重写compareTo方法。实现了Comparable的类具有自比较功能,类的一个对象自己就可以通过compareTo方法与类中另一个对象(或仍是自身)进行比较。
而Comparator是一个外比较器,需要构建新的Comparator类来实现。作为compare方法的参数的两个对象自身可以不具备自比较功能,实现Comparator的类可以被看作一种算法的实现,作为专用的比较器为其提供比较方式。
实现了Comparable接口的类的对象构成的list可以直接使用Collections.sort(list)进行排序。如果list中元素所处类没有实现Comparable接口或实现了Comparable接口但想要以其他专门规则进行排序,需要使用Collections.sort(list,
comparator)。具体会在下面进行分析。
在Collection类中有重载方法sort实现排序功能。
Collection类中的重载方法sort:
第一个Sort方法只要求一个参数list,方法的功能是按自然顺序对list中的元素进行排序。第二个Sort方法要求除list外还应有比较器c,方法的功能是使用比较器c定义的专门规则进行排序。
实际上在List接口中实现的sort方法还进一步调用了Arrays类中的sort方法。Arrays类中针对多种元素类型的list和提供的不同参数信息重载了多种sort方法,部分sort方法又进一步调用了快排等排序功能的类中的函数。详细解读比较费劲。
在Arrays类中最受关注的应该是下面这部分:
Arrays中自己定义了一个比较器NaturalOrder,其实例为NaturalOrder.INSTANCE,比较方式是使用待比较的list中元素所属类中的compareTo方法,比较器名就是自然顺序。
考虑sort方法的实现过程。除了基本类型排序最终使用快排等方法进行排序外,对于大部分的其他类型元素进行排序时,最终发挥比较功能的方法大多需要一个比较器。如果调用Collections.sort时不提供比较器,则list接口中的sort方法传递给Arrays中被调用的sort方法的比较器为null。Arrays中的sort方法会判断调用者是否提供了比较器。如果提供了比较器,Arrays中的方法在调用其他方法时将提供的比较器逐层传递,最终发挥实际排序功能的方法会用此比较器进比较排序,这样排序是按比较器提供的专门规则进行的,排序时使用的比较方法是Comparator中的compare。如果没有提供比较器,即比较器参数值为null,在调用最终发挥比较功能的方法时会默认将NaturalOrder.INSTANCE作为比较器去调用,这样最终发挥实际排序功能的方法排序后得到的排序结果是按自然顺序进行的,实际排序时使用的比较方法是Comparable中的compareTo。
实现Comparable:
public class test implements Comparable<test>{ public int first; public int second; public test(int first,int second){ this.first=first; this.second=second; } @Override public int compareTo(test o) { if(this.first<o.first) return -1; if (this.first==o.first) return 0; else return 1; } }
测试:
public class main { public static void main(String[] args) { test test1=new test(1,9); test test2=new test(2,7); System.out.println(test1.compareTo(test2));//期望是-1,1<2 System.out.println(test1.compareTo(test1));//期望是0 两个相同对象进行比较 } }
测试结果:
实现Comparator:
import java.util.Comparator; /** * 比较test.first * */ public class Comparator1 implements Comparator<test> { @Override public int compare(test o1, test o2) { if(o1.first<o2.first) return -1; if (o1.first==o2.first) return 0; else return 1; } }
import java.util.Comparator; /** * 比较test.second * */ public class Comparator2 implements Comparator<test> { @Override public int compare(test o1, test o2) { if(o1.second<o2.second) return -1; if (o1.second==o2.second) return 0; else return 1; } }
测试:
public class main { public static void main(String[] args) { test test1=new test(1,9); test test2=new test(2,7); /*System.out.println(test1.compareTo(test2));//期望是-1,1<2 System.out.println(test1.compareTo(test1));//期望是0 两个相同对象进行比较*/ System.out.println(new Comparator1().compare(test1,test2));//期望是-1,1<2 System.out.println(new Comparator2().compare(test1,test2));//期望是1 9>7 } }
测试结果:
以自然顺序(test.first的大小)进行排序:
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class main { public static void main(String[] args) { test test1=new test(1,9); test test2=new test(2,7); test test3=new test(0,8); List<test> list=new ArrayList<>(); list.add(test1); list.add(test2); list.add(test3); Collections.sort(list); for(test temp:list){ System.out.println(temp.first+" "+temp.second); } } }
排序结果:
在这里要关注一点,如果以自然顺序进行排序,即使用 Collections.sort(list);语句,如果test类没有实现Comparable,编译时会报错。
以Comparator2特定规则(test.second大小)进行排序:
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class main { public static void main(String[] args) { test test1=new test(1,9); test test2=new test(2,7); test test3=new test(0,8); List<test> list=new ArrayList<>(); list.add(test1); list.add(test2); list.add(test3); Collections.sort(list,new Comparator2()); for(test temp:list){ System.out.println(temp.first+" "+temp.second); } } }
排序结果:
从这里可以看出,使用Comparator会使得含有多个属性值的类的对象之间的比较更加的灵活多样。