本文摘抄至 : 小龙猫
摘要 : 本文通过分析String和Integer实现Comparable接口的实现。和自定义分别实现Comparable和Comparator来区分两者的差异
一、实现Comparable接口
Comparable是java.lang包下的一个接口,该接口里只有一个compareTo()方法 :
package java.lang;
import java.util.*;
public interface Comparable<T> {
public int compareTo(T o);
}
Comparable翻译为“”可比较的“”,表明实现该接口的类都是可以比较的。即实现Comparable接口的类本身就已经支持比较。例如 : String、Integer 自己就可以完成比较大小的操作。他们已经实现了Comparable接口。查看String类的源码可以看见这样声明的(JDK1.7) :
public final class String implements java.io.Serializable, Comparable<String>, CharSequence
而Integer类的声明如下:
public final class Integer extends Number implements Comparable<Integer>
可见它们都实现了Comparable接口,它们的实例都可以通过调用自身的compareTo()方法来比较自己,例如:
public static void main(String[] args) {
String s1 = "aa" ;
String s2 = "cc";
//返回是两个字符串第一个不同的 char的unicode编码的差
System.out.println(s1.compareTo(s2));
System.out.println(s2.compareTo(s1));
System.out.println("aa".compareTo(s1));
Integer i1 = 3 ;
Integer i2 = 2 ;
//前者大返回 1,后着大返回 -1,相等返回 0
System.out.println(i1.compareTo(i2));
System.out.println(i2.compareTo(i1));
System.out.println(new Integer(3).compareTo(i1));
}
--------- output -------------
-2
2
0
1
-1
0
而String类中的compareTo方法时这样实现的:
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) {
//返回第一个不同字母的Unicode的差
return c1 - c2;
}
k++;
}
//如果两个字符串只有长度不同,则返回长度的差
return len1 - len2;
}
Integer类中的compareTo()方法的实现如下:
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
其compare()方法如下:
public static int compare(int x, int y) {
//前者小返回 -1,相等返回 0, 前者大返回 1
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
因此,我们可以参照String类及Integer类对Comparable接口的实现来实现自己的比较方式,然后调用compareTo()方法即可知道他们的顺序。下面是一个例子(按Person类的年龄排序):
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);
for (int i = 0; i < persons.length; i++)
System.out.println("第"+i+"元素 = "+persons[i]);
System.out.println("----------------------------");
//下面代码的结果一样
List<Person> list = Arrays.asList(persons) ;
Collections.sort(list);
for(Iterator it = list.iterator(); it.hasNext();)
System.out.println("list 中元素有 :" +it.next());
}
}
class Person implements Comparable<Person>{
private int age;
private String name ;
public Person(int age,String name){
this.age = age ;
this.name = name ;
}
//实现Comparable接口的compareTo方法
@Override
public int compareTo(Person o) {
// TODO Auto-generated method stub
return this.age - o.age;
}
@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + "]";
}
}
输出结果如下 :
通过以上结果,我们可以得知Person对象已经通过Arrays.sort()或ollections.sort(list)方法按照age排序。
二、实现Comparator接口
Comparator是java.util包下的一个接口,该接口里有两个方法compare()和equals():
package java.util;
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
Comparator翻译为“比较器”,表明实现该接口的类都是一个比较器,一般在要比较的对象类型不支持自比较或者自比较函数不能满足要求时使用。使用此接口时,我们可以不在需要比较的类型(这里是Person类)中实现比较过程,而是把这个过程转移到了Comparator接口的compare方法中。于是,上面的例子需要修改为:
public class ComparatorTest {
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;
}
@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + "]";
}
}
//这是一个比较器,用于比较Person对象
class PersonComparator implements Comparator<Person>{
@Override
public int compare(Person o1, Person o2) {
//两个Person对象的比较过程,当然,这里可以实现更多更复杂的比较过程
//如果o1.age > o2.age,方法返回正数,为正数正是表明哦o1 > o2
//如果o1.age = o2.age,方法返回0,返回0正是表明o1 == o1
//如果o1.age < o2.age,方法返回正数,为负数正是表明哦o1 < o2
return o1.getAge() - o2.getAge();
}
}
---------- output ------------
Before sort:
[Person [age=20, name=P1], Person [age=60, name=P2], Person [age=50, name=P3], Person [age=40, name=P4]]
After sort:
[Person [age=20, name=P1], Person [age=40, name=P4], Person [age=50, name=P3], Person [age=60, name=P2]]
由此可见,我们可以将一个自定义的比较器作为参数传递给Collections.sort()方法,下图是API中的Collections.sort()方法:
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)来比较两个对象;
可以说一个是自己完成比较,一个是外部程序实现比较的差别而已。
比如:你想对整数采用绝对值大小来排序,Integer 是不符合要求的,你不需要去修改 Integer 类(实际上你也不能这么做)去改变它的排序行为,这时候只要(也只有)使用一个实现了 Comparator 接口的对象来实现控制它的排序就行了。
两种方式,各有各的特点:使用Comparable方式比较时,我们将比较的规则写入了比较的类型中,其特点是高内聚。但如果哪天这个规则需要修改,那么我们必须修改这个类型的源代码。如果使用Comparator方式比较,那么我们不需要修改比较的类,其特点是易维护,但需要自定义一个比较器,后续比较规则的修改,仅仅是改这个比较器中的代码即可。