上一篇文章讲了Comparable接口的使用,建议搭配食用。
在实现Comparable接口的前提下,对象间已经有一套可适用的大小比较规则/排序规则了。然而某些情况下,由compareTo定义的排序规则不再适用,需要一套新的规则,我们该怎么做呢?
修改源码中的compareTo方法或许是可行的,然而这样的弊端也颇多:
可见,改源码不见得是一个好的选择。而JDK为了解决这个问题提供了更具普适性的方案:Comparator接口。
这个长得非常像Comparable的和它一样,也是个带泛型的接口。
compare方法和compareTo方法长得也很像,两者主要区别是前者含两个形参,后者仅有一个。
两者主要在应用方式上有较大的区别,下面将通过举例来详细叙述这一部分。
我们知道字符串String默认的排序顺序是由小到大进行排序,那当我们需要其由大到小进行排序,应该怎么做?
@Test
public void test3(){
String[] arr = new String[]{"AA","BB","ZZ","DD","CC"};
Arrays.sort(arr, new Comparator<String>() {
// 按照字符串从大到小的顺序排列
@Override
public int compare(String o1, String o2) {
return -o1.compareTo(o2);
}
});// 这是个匿名类
System.out.println(Arrays.toString(arr));
// [ZZ, DD, CC, BB, AA]
}
仔细观察上端代码,不难发现其与对String直接排序的区别在于Array.sort方法新增了一个参数,该位置要求传入一个Comparator接口的实现对象。这里通常为简洁起见会直接使用一个匿名类充当。
定义匿名类后重点在于需要重写其compare方法,重写规则与compareTo方法相同,仅仅是形参变成两个。在数组排序中,o1是指前一个元素,o2是指后一个元素。
重写规则:o1大于o2时,返回正整数;反之返回负整数,相等返回0。与compareTo的重写规则相同。
总结来说,使用Comparator接口分为以下几个步骤:
清楚基本的使用方法后,我们再看看其在自定义类中的使用。
首先我们自定义了这么个类:
public class Product implements Comparable {
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
...
该类重写了compareTo方法,按产品价格从低到高进行排序。具体如下:
@Override
public int compareTo(Product o) {
if (this.price > product.price)
return 1;
else if (this.price < product.price)
return -1;
else
return 0;
} // 这种写法是按价格从低到高进行排序
重写了compareTo方法后通过sort我们可以直接对Product的数组按价格升序进行排序。而倘若这时候我们需要按名称进行排序,同时若名称相同则按价格进行二级排序呢?
@Test
public void test4(){
Product[] arr = new Product[4];
arr[0] = new Product("A",59);
arr[1] = new Product("B",12);
arr[2] = new Product("C",44);
arr[3] = new Product("D",102);
Arrays.sort(arr, new Comparator<Product>() {
@Override
public int compare(Product o1, Product o2) {
if (o1.getName().equals(o2.getName())){
return -Double.compare(o1.getPrice(),o2.getPrice());
}else {
return o1.getName().compareTo(o2.getName());
}
}
}); // 未实现Comparable接口会抛异常
System.out.println(Arrays.toString(arr));
}
其实与上文提到的String类操作大同小异,无非是重写函数的内部稍微复杂了些。
基于上述内容,我们可以来个小的总结。