Java 中基本类型的比较可以使用比较运算符,不同于C++,Java 中的对象比较是不能利用运算符重载(尽管 Java 中有运算符重载现象)。Comparable 和 Comparator 接口的存在就是为了对象比较,我们可以在接口中定义对象比较规则,还可以利用 Collections.sort 和 Arrays.sort 方法对对象数组和集合进行排序。
Comparable接口
该接口位于 java.lang 包下,接口中只有一个抽象方法 compareTo(),下面就是接口的源码 :
package java.lang;
import java.util.*;
public interface Comparable<T> {
public int compareTo(T o);
}
对于实现该接口的类,会被强加一个整体排序,排序规则在 compareTo 方法中进行定义,也称这种排序方式为自然排序。
怎么使用这个接口呢?这里通过一个相亲的例子看我们如何通过 Comparable 接口给相亲对象做一个简单排序。一般相亲,女方会看男方有没有钱,年龄有多大等等之类的,当然,也不排除男方会在意女方有没有钱……
首先,我们定义一个 Person 类并实现 Comparable 接口:
public class Person implements Comparable<Person>{
String name;
int age;
int money;
public Person(String name, int age, int money) {
this.name = name;
this.age = age;
this.money = money;
}
@Override
public int compareTo(Person another) {
//这里我们选择用金钱来对这个相亲对象进行排序,还是比较现实的哈
if(this.money > another.money)
return 1;
else if(this.money == another.money)
return 0;
else
return -1;
}
}
Tips:Comparable 泛型接口中的 T 在使用时,须指明为要比较的对象类型,例如 Person
在 Person 类中,我们已经定义好了规则,假设只有两个相亲对象 Jack 和 Timi,可以直接通过 compareTo 这个方法就可以完成比较:
public class Test {
public static void main(String[] args) {
Person Jack = new Person("Jack", 25, 3000000);
Person Timi = new Person("Timi", 20, 2000000);
if(Timi.compareTo(Jack) > 0)
System.out.println("My choice is\r" + Timi.name);
else
System.out.println("My choice is\r" + Jack.name);
}
}/*output:
My choice is
Jack
*/
Tips:自然排序中的 a.compareTo(b) == 0 等价于 a.equals(b),但是 compareTo 中不允许比较 null ,否则会抛出空指针异常。
在比较规则下,当然是 Jack 比 Timi 有钱,选 Jack 没烦恼;假设相亲对象不止一个,大约有一个数组那么多,可以使用 Arrays.sort 进行排序:
public class Test {
public static void main(String[] args) {
Person[] men = new Person[]{
new Person("Jack",25,3000000),
new Person("Tom",20,2000000),
new Person("Siri",31,10000000),
new Person("Nacy",26,2500000)
};
Arrays.sort(men);
for(Person man:men) {
System.out.println(man.name +"\t"+ man.age +"\t"+ man.money);
}
}
}/*output:
Tom 20 2000000
Nacy 26 2500000
Jack 25 3000000
Siri 31 10000000
*/
如果不进行排序,相亲对象在数组中就会杂乱不堪,使用 Arrays.sort(Object[] a) 方法进行排序后,对象们根据财力的从小到大进行了一个排序。假如我们想要将财力从大到小排序,只需要修改一个比较规则,即 compareTo 方法:
public int compareTo(Person another) {
if(this.money < another.money)
return 1;
else if(this.money == another.money)
return 0;
else
return -1;
}
因为,Arrays.sort 只能进行对象数组的排序,如果相亲对象是放在集合中,可以使用 Collections.sort(List list) 进行排序:
public class Test {
public static void main(String[] args) {
ArrayList<Person> men = new ArrayList<>();
men.add(new Person("Jack",25,3000000));
men.add(new Person("Tom",20,2000000));
men.add(new Person("Siri",31,10000000));
men.add(new Person("Nacy",26,2500000));
Collections.sort(men);
for(Person man:men) {
System.out.println(man.name +"\t"+ man.age +"\t"+ man.money);
}
}
}
Tips:使用 Arrays.sort 和 Collections.sort 进行对象排序时,所要排序的对象必须实现了 Comparable 接口。
在 java.lang 包中,可以看到基本类型的包装类们,都实现了 Comparable 接口,它们都定义了自己的比较规则。
Comparator接口
该接口位于 java.util 包下,这个接口中的方法还是比较多的,如果我们想要使用这个接口,只需要实现一个抽象方法 compare 就可以了:
public interface Comparator<T> {
int compare(T o1, T o2);
}
对于实现该接口的类,我们称为比较器类,在使用 Arrays.sort 和 Collections.sort 方法进行排序时,只需要将要比较的对象和比较器传入方法,在比较器的 compare 方法中定义了比较规则,我们也称此排序方式为比较器排序。
下面就是使用比较器,对相亲对象进行排序。首先,我们就是要删除 Person 类中的比较规则:
public class Person{
String name;
int age;
int money;
public Person(String name, int age, int money) {
this.name = name;
this.age = age;
this.money = money;
}
}
然后,我们定义一个 Person 类的比较器,需要实现 Comparator 类:
public class PersonComparator implements Comparator<Person>{
@Override
public int compare(Person o1, Person o2) {
if(o1.money > o2.money)
return 1;
else if(o1.money == o2.money)
return 0;
else
return -1;
}
}
最后,我们使用 Arrays.sort(T[] a, Comparator<? super T> c) 方法 和 Collections.sort(List list, Comparator<? super T> c) 方法给对象们进行一个财力排序:
public class Test {
public static void main(String[] args) {
/*Person[] men = new Person[]{
new Person("Jack",25,3000000),
new Person("Tom",20,2000000),
new Person("Siri",31,10000000),
new Person("Nacy",26,2500000)
};*/
ArrayList<Person> men = new ArrayList<>();
men.add(new Person("Jack",25,3000000));
men.add(new Person("Tom",20,2000000));
men.add(new Person("Siri",31,10000000));
men.add(new Person("Nacy",26,2500000));
// Arrays.sort(men, new PersonComparator());
Collections.sort(men, new PersonComparator());
for(Person man:men) {
System.out.println(man.name +"\t"+ man.age +"\t"+ man.money);
}
}
}/*output:
Tom 20 2000000
Nacy 26 2500000
Jack 25 3000000
Siri 31 10000000
*/
如果我们仅仅对两个相亲对象就行比较,就不能用 compareTo 方法了,理智告诉我们,Person 类中已经没有了比较规则。
那么,如果我们想要对这两个相亲对象进行比较,而又不使用对象数组或者集合,我们又该怎么实现?
public class Test {
public static void main(String[] args) {
Person Jack = new Person("Jack", 25, 3000000);
Person Timi = new Person("Timi", 20, 2000000);
PersonComparator comparator = new PersonComparator();
if(comparator.compare(Jack, Timi) > 0) {
System.out.println("My choice is\r" + Jack.name);
}else {
System.out.println("My choice is\r" + Timi.name);
}
}
}/*output:
My choice is
Jack
*/
如果查看 java.util 包中的集合类,我们会发现:集合类中的 sort 方法大都支持传入一个自定义的比较器,来改变默认的排序方式。
总结
1. compareTo 方法或者 compare 方法的返回值,都不要求一定返回 -1, 0, 1 ;我们只需要返回一个 正整数、负整数或者 0 即可。
所以上面的 comparaTo 方法和 comparable 方法实际上可以写成差值的形式:
public int compare(Person o1, Person o2) {
return o1.money - o2.money;
}
2. 使用 Comparable 和 Comparator 接口时抛出空指针异常,并不代表我们不能比较空对象,只要我们在对应的方法中给出处理空对象的规则,同样可以进行下去:
public int compareTo(Person another) {
//一个不存在的相亲对象肯定比不过这些实实在在的相亲对象
if(another == null)
return -1;
if(this.money > another.money)
return 1;
else if(this.money == another.money)
return 0;
else
return -1;
}
我们可以简单测试一下,空对象处理方法:
public class Test {
public static void main(String[] args) {
Person Jack = new Person("Jack", 25, 3000000);
Person Timi = new Person("Timi", 20, 2000000);
if(Timi.compareTo(null) > 0)
System.out.println("My choice is\r null");
else
System.out.println("My choice is\r" + Jack.name);
}
}/*output:
My choice is
Jack
*/
3. Comparable 和 Comparator 的区别:实现 Comparable 的类,说明这个类是可比较的;实现 Comparator 的类变成了一个工具类,需要比较的类可以使用这个工具类进行比较。