Java作为一门面向对象的编程语言,里面的一切几乎都是由类和对象组成的。而正常情况下,对象之间只能进行比较操作( == 或 != ),而不能使用诸如>,<等判断大小的符号。
但是在实际的开发应用中,对对象排序的需求又确实存在,而排序则必须要确立一种比较大小的方式,因此Comparable接口便应运而生。
首先我们先弄清楚一件事:Comparable是一个接口,当一个类的对象间有排序或比较的需求时,就可以令这个类实现这个接口。注意,这个接口是带泛型的,泛型指定的是需要比较的类。
public class Product implements Comparable<Product> {...
该接口里只有一个方法:compareTo(T o),T表示泛型,只需要重写了这个方法,即可实现对象之间的比较与排序。
@Override
public int compareTo(Product o) {...
需要注意的是,如String、包装类等已经实现了Comparable接口并重写了compareTo方法,默认进行的是从小到大的排列。
JDK1.8中String类重写的toString方法:
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;
}
k++;
}
return len1 - len2;
}
对String的排序进行简单的演示:
@Test
public void test1(){
String[] arr = new String[]{"AA","BB","ZZ","DD","CC"};
Arrays.sort(arr); // 对arr数组进行排序
System.out.println(Arrays.toString(arr)); // [AA, BB, CC, DD, ZZ]
}
既然需要我们对compareTo方法进行重写,那么我们也需要知道其中一些规则。
因此,了解了这些基础的规则后,我们便可以通过控制compareTo方法返回值的方式,人为地定义两个对象的大小。
如上文所示,为了演示我们不妨定义一个Product类,同时使其实现Comparable接口:
public class Product implements Comparable {
private String name;
private double price;
public Product(String name, double price) {
this.name = name;
this.price = price;
}
...
这个类中有两个属性:名称name与价格price。参考现实的情况,当我们在电商平台购物时,往往会让商品以价格升序或降序的方式进行排序供我们挑选。因此,假定我们有了个需求:对Product的实例化对象按照他们的价格进行排序。
为了实现这个目的,我们需要恰当地重写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;
} // 这种写法是按价格从低到高进行排序
当然,这段代码可以写得再精简一点,不过这就是题外话了。
return Double.compare(this.price,o.price);
测试一下:
@Test
public void test2(){
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); // 排序,未实现Comparable接口时会抛异常
System.out.println(Arrays.toString(arr));
// [Goods{name='B', price=12.0}, Goods{name='C', price=44.0}, Goods{name='A', price=59.0}, Goods{name='D', price=102.0}]
}
可以看到排序后的数组中Product对象的确是以价格升序进行排列的。
若我们需要按价格降序排序?很简单,在原有代码的基础上return的整数前添个负号即可:
return -Double.compare(this.price,o.price);
上述代码已经初步达成了我们的需求,而更进一步地想,当两个Prodect对象的价格相同时,是否可以实现按其名称关系再进行排序?这就引出我们的下一步操作:多级排序。
同样的案例,以二级排序为例,其实很简单,在源代码基础上稍作修改即可。代码如下:
@Override
public int compareTo(Product o) {
if (this.price > product.price)
return 1;
else if (this.price < product.price)
return -1;
else
return this.name.compareTo(o.name);
}
只需要修改else下的情况,令两个对象的name再比较一次即可。
这里String类的name是自身已实现了Comparable接口的类,所以可以直接调用其的compareTo方法。若比较的是其他自定义类,则需要该类也实现了Comparable接口或者直接在当前代码块下手动补充大小的比较标准。
同样道理,利用这种多层嵌套的方式可以实现更高层次的多级排序。
推荐搭配食用:Comparator/compare,一次学俩。