Comparable与Comparator的区别

简介:

    前几天在项目中遇到了一个将复杂对象进行排序的问题:计算BingMap地图上距离当前位置5KM内发生事故(TrafficIncident)的点到当前位置的距离,并按距离升序排序。距离都算出来了,但这些TrafficIncident对象的排序却难到了我。经同事提醒,Comparable或Comparator是一个不错的选择。于是在网上搜索了一些资料,总结下来。

方式一: 实现Comparable接口

    Comparable是java.lang包下的一个接口,该接口里只有一个compareTo()方法:

?
1
2
3
4
5
6
package  java.lang;
import  java.util.*;
 
public  interface  Comparable<T> {
     public  int  compareTo(T o);
}

    Comparable翻译为“可比较的”,表明实现该接口的类都是可以比较的,即实现Comparable接口的类本身就已经支持自比较,例如: String、Integer 自己就可以完成比较大小操作,它们已经实现了Comparable接口。查看String类的源码可以看见是这样声明的(JDK1.7):

?
1
2
public  final  class  String
     implements  java.io.Serializable, Comparable<String>, CharSequence

    而Integer类的声明如下:

?
1
public  final  class  Integer  extends  Number  implements  Comparable<Integer>

    可见它们都实现了Comparable接口,它们的实例都可以通过调用自身的compareTo()方法来比较自己,例如:

?
1
2
3
4
5
6
String s1 =  "aa" ;
String s2 =  "ab" ;
System.out.println(s1.compareTo(s2));  //返回是两字符串第一个不同的char的unicode编码的差
Integer i1 =  3 ;
Integer i2 =  2 ;
System.out.println(i1.compareTo(i2));  //前者大返回1,后者大返回-1,相等返回0

    而String类中的compareTo方法时这样实现的:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public  int  compareTo(String anotherString) {
     int  len1 = value.length;
     int  len2 = anotherString.value.length;
     int  lim = Math.min(len1, len2);
     char  v1[] = value;
     char  v2[] = anotherString.value;
 
     int  k =  0 ;
     while  (k < lim) {
         char  c1 = v1[k];
         char  c2 = v2[k];
         if  (c1 != c2) {
             return  c1 - c2; //返回第一个不同字母的unicode的差
         }
         k++;
     }
     return  len1 - len2; //如果两个字符串只有长度不同,则返回长度的差
}

    Integer类中的compareTo()方法的实现如下:

?
1
2
3
4
5
6
public  int  compareTo(Integer anotherInteger) {
     return  compare( this .value, anotherInteger.value);
}
public  static  int  compare( int  x,  int  y) {
     return  (x < y) ? - 1  : ((x == y) ?  0  1 );  //前者大返回1,后者大返回-1,相等返回0
}

    因此,我们可以参照String类及Integer类对于Comparable接口的实现来实现自己的比较方式,然后调用compareTo方法即可知道他们的顺序。 下面是一个例子(按Person类的年龄排序):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import  java.util.Arrays;
 
public  class  ComparableTest {
     public  static  void  main(String[] args) {
         Person[] persons =  new  Person[]{
             new  Person( 20 "P1" ),
             new  Person( 60 "P2" ),
             new  Person( 50 "P3" ),
             new  Person( 40 "P4" )
         };
         Arrays.sort(persons);
         System.out.println();
         //下面代码的结果一样
         //List<Person> personList = Arrays.asList(persons);
         //Collections.sort(personList);
         //System.out.println(personList);
     }
}
 
class  Person  implements  Comparable<Person> {
     private  int  age;
     private  String name;
     public  Person( int  age, String name) {
         this .age = age;
         this .name = name;
     }
     public  int  getAge() {
         return  age;
     }
     public  void  setAge( int  age) {
         this .age = age;
     }
     public  String getName() {
         return  name;
     }
     public  void  setName(String name) {
         this .name = name;
     }
     //实现Comparable接口的compareTo方法
     @Override
     public  int  compareTo(Person o) {
         return  this .age - o.age;
     }
     
     @Override
     public  String toString() {
         return  "Person[name="  + name +  ", age="  + age +  "]" ;
     }
}

    执行结果是:

?
1
[Person[name=P1, age= 20 ], Person[name=P4, age= 40 ], Person[name=P3, age= 50 ], Person[name=P2, age= 60 ]]

    由此可见,Person对象已经通过Arrays.sort()方法按照age排序。


方式二: 实现Comparator接口

    Comparator是java.util包下的一个接口,该接口里有两个方法compare()和equals():

?
1
2
3
4
5
6
package  java.util;
 
public  interface  Comparator<T> {
     int  compare(T o1, T o2);
     boolean  equals(Object obj);
}

    Comparator翻译为“比较器”,表明实现该接口的类都是一个比较器,一般在要比较的对象类型不支持自比较或者自比较函数不能满足要求时使用。使用此接口时,我们可以不在需要比较的类型(这里是Person类)中实现比较过程,而是把这个过程转移到了Comparator接口的compare方法中。于是,上面的例子需要修改为:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import  java.util.Arrays;
import  java.util.Collections;
import  java.util.Comparator;
import  java.util.List;
 
public  class  ComparableTest {
     public  static  void  main(String[] args) {
         Person[] persons =  new  Person[]{
             new  Person( 20 "P1" ),
             new  Person( 60 "P2" ),
             new  Person( 50 "P3" ),
             new  Person( 40 "P4" )
         };
         //Arrays.sort方法不支持使用Comparator比较器了,这里只能使用Collections.sort来排序
         List<Person> personList = Arrays.asList(persons);
         System.out.println( "Before sort: \r\n"  + personList);
         //这里,将一个比较器(Comparator)传递给sort方法作为参数,按照里面的比较逻辑对Person进行排序
         Collections.sort(personList,  new  PersonComparator());
         System.out.println( "After sort: \r\n"  + personList);
     }
}
 
//被比较的类型不需要实现任何接口
class  Person { 
     private  int  age;
     private  String name;
     public  Person( int  age, String name) {
         this .age = age;
         this .name = name;
     }
     public  int  getAge() {
         return  age;
     }
     public  void  setAge( int  age) {
         this .age = age;
     }
     public  String getName() {
         return  name;
     }
     public  void  setName(String name) {
         this .name = name;
     }
     @Override
     public  String toString() {
         return  "Person[name="  + name +  ", age="  + age +  "]" ;
     }
}
 
//这是一个比较器,用于比较Person对象
class  PersonComparator  implements  Comparator<Person> {
     @Override
     public  int  compare(Person o1, Person o2) {
         //两个Person对象的比较过程,当然,这里可以实现更多更复杂的比较过程
         return  o1.getAge() - o2.getAge();    //如果o1.age > o2.age,方法返回正数,为正数正是表明哦o1 > o2
                                             //如果o1.age = o2.age,方法返回0,返回0正是表明o1 == o1
                                             //如果o1.age < o2.age,方法返回正数,为负数正是表明哦o1 < o2
     }
}

    输出结果为:

?
1
2
3
4
Before sort: 
[Person[name=P1, age= 20 ], Person[name=P2, age= 60 ], Person[name=P3, age= 50 ], Person[name=P4, age= 40 ]]
After sort: 
[Person[name=P1, age= 20 ], Person[name=P4, age= 40 ], Person[name=P3, age= 50 ], Person[name=P2, age= 60 ]]

    由此可见,我们可以将一个自定义的比较器作为参数传递给Collections.sort()方法,下图是API中的Collections.sort()方法:

200740_FAHb_1434710.jpg

    Person对象已经按照比较器中的规则进行排序。如果在使用Collections.sort()方法时不提供这个Comparator(其实就是使用前文介绍的排序方式),那么就以自然顺序排序,如Collections.sort()方法的API所说:Sorts the specified list into ascending order, according to the natural ordering of its elements. All elements in the list must implement the Comparable interface. 这里的自然顺序就是实现Comparable接口设定的排序方式。

    细心的同学已经看到,Comparator接口中还有个equals()方法没有实现,可程序并没有报错,原因是实现该接口的类也是Object类的子类,而Object类已经实现了equals方法


相同点

  1. 都是用于比较两个对象“顺序”的接口

  2. 都可以使用Collections.sort()方法来对对象集合进行排序

不同点

  1. Comparable位于java.lang包下,而Comparator则位于java.util包下

  2. Comparable 是在集合内部定义的方法实现的排序,Comparator 是在集合外部实现的排序

总结

    使用Comparable接口来实现对象之间的比较时,可以使这个类型(设为A)实现Comparable接口,并可以使用Collections.sort()方法来对A类型的List进行排序,之后可以通过a1.comparaTo(a2)来比较两个对象

    当使用Comparator接口来实现对象之间的比较时,只需要创建一个实现Comparator接口的比较器(设为AComparator),并将其传给Collections.sort()方法即可对A类型的List进行排序,之后也可以通过调用比较器AComparator.compare(a1, a2)来比较两个对象。

    可以说一个是自己完成比较,一个是外部程序实现比较的差别而已

    用 Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。

    比如:你想对整数采用绝对值大小来排序,Integer 是不符合要求的,你不需要去修改 Integer 类(实际上你也不能这么做)去改变它的排序行为,这时候只要(也只有)使用一个实现了 Comparator 接口的对象来实现控制它的排序就行了。

    两种方式,各有各的特点:使用Comparable方式比较时,我们将比较的规则写入了比较的类型中,其特点是高内聚。但如果哪天这个规则需要修改,那么我们必须修改这个类型的源代码。如果使用Comparator方式比较,那么我们不需要修改比较的类,其特点是易维护,但需要自定义一个比较器,后续比较规则的修改,仅仅是改这个比较器中的代码即可。


目录
相关文章
|
3月前
|
Java
Java中的比较器Comparable与Comparator
Java中的比较器Comparable与Comparator
|
4月前
|
Java
Comparable和Comparator两种比较器详解
Comparable和Comparator两种比较器详解
19 0
|
8月前
Comparable与Comparator对象比较
Comparable与Comparator对象比较
30 0
Comparable 和 Comparator的理解
Comparable是一个排序接口 此接口给实现类提供了一个排序的方法,此接口有且只有一个方法
95 0
Comparable 和 Comparator的理解
排序Comparable 和 Comparator的区别
排序Comparable 和 Comparator的区别
|
搜索推荐 算法 安全
浅谈Comparable和Comparator
首先我们考虑一个场景:有一个整形数组, 我们希望通过调用一个工具类的排序方法就能对该数组进行排序. 请看下面的代码:
Comparable与Comparator有什么区别?
Comparable与Comparator有什么区别?
177 0
关于Comparator和Comparable的理解
关于Comparator和Comparable的理解
87 0
|
Java
Comparable接口和Comparator接口
Comparable接口和Comparator接口
182 0
Comparable接口和Comparator接口