Comparable和Comparator介绍以及常见的问题
在实际业务开发中,常常看到使用compareTo,compare方法进行值得比较、排序,compareTo方法来自于Comparable接口,而compare方法则来自于Comparator接口,两个接口都是顶层接口。Comparable翻译为中文是“比较”的意思,而Comparator是“比较器”的意思。Comparable是以-able结尾的,表示它自身具备着某种能力,而Comparator是以-or结尾,表示自身是比较的参与者。虽然Comparable和Comparator都是用来进行元素排序的,但二者有着本质的区别,
Comparable
常见类值比较
如Integer、String、BigDecimal得比较
int c = Integer.valueOf(1).compareTo(XXX);
int s = String.valueOf(1).compareTo(YYY);
int b = BigDecimal.valueOf(1).compareTo(FFF);
当返回得值大于0,表示前面得值大于后面得值,当返回得值等于0,表示前面得值等于后面得值,当返回得值小于0,表示前的值小于后面得值。
问题1:使用compareTo方法判断两个值得是否相等有什么意义,用equals也能实现进行判断得呀?
解答:通常,仅仅知道两个值是否相同是不够的。对于排序应用来说,必须知道一个值是大于、等于还是小于另一个,从而知道先后顺序。compareTo() 方法实现了这种功能。
int compareToResult = Integer.valueOf(1).compareTo(XXX);
if(compareToResult == 0){
do1.....
}else if(compareToResult > 0){
do2.....
}else if(compareToResult < 0){
do3.....
}
这显然使用equals是不能达到这种效果
问题2:当compareTo入参为null会怎么样?
解答:当compareTo入参为null时会导致空指针异常,故我们需要避免入参是null得情况
问题2:可以自定义重写compareTo吗?
解答:可以,compareTo方法其实是Java.lang.Comparable接口定义的方法,它有一个参数即要比较的对象。这个方法返回一个整数值,用于指示被比较的对象的顺序关系。如果当前对象小于要比较的对象,则返回负整数;如果当前对象等于要比较的对象,则返回0;如果当前对象大于要比较的对象,则返回正整数。返回的值用于排序和搜索等操作。
Comparable接口
public interface Comparable<T> {
public int compareTo(T o);
}
比如String、Integer、BigDecimal都是实现了这个Comparable接口,并重写了compareTo方法
String
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
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;
}
}
Integer
public final class Integer extends Number implements Comparable<Integer> {
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);
}
}
。。。
自定义类比较
如Integer、String、BigDecimal类都是继承了Comparable并重写了compareTo方法进行比较,当然我们也可以自定义类值的比较策略
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person implements Comparable<Person>{
private String name;
private Integer age;
@Override
public int compareTo(Person p) {
if(Objects.isNull(p)||Objects.isNull(p.getAge())){
//待比较值为空,无法比较,返回 999
return 999
}
return this.getAge().compareTo(p.getAge());
}
}
public String compareToPersion(Person p1,Person p2){
int compareAge = (Objects.isNull(p)||Objects.isNull(p.getAge())) ? 999 : p1.compareTo(p2);
if(compareAge == 0){
return p1.getName + "和" + p2.getName + "年龄相同!";
}else if(compareAge > 0){
return p1.getName + "的年龄大于!" + p2.getName;
}else if(compareAge < 0){
return p1.getName + "的年龄小于" + p2.getName;
}else{
return "数据异常,无法比较!";
}
}
排序功能使用
自然排序
在上述,我们已经见过了Integer、String、BigDecimal等对象是实现了Comparable接口并重写CompareTo方法,可以实现自然排序,这是Java中非常重要的概念之一。所谓自然排序,就是按照对象的自然顺序进行排序
我们先看如下代码
List<String> list = new ArrayList<>();
list.add("add");
list.add("bean");
list.add("port");
Collections.sort(list);
System.out.println(list);
Collections的sort排序底层实现是什么呢?按照什么规则排序呢?
Collections的sort源码
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
Collections类的参数只有一个集合的sort方法,集合的T对象继承于Comparable接口,即默认使用T对象的compareTo方法进行排序。比如Integer、String、BigDecimal对象等的默认排序,当对象T未继承Comparable接口,则使用默认排序,可以追踪一下该方法的源码
自定义排序
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person implements Comparable<Person>{
private String name;
private Integer age;
@Override
public int compareTo(Person p) {
return this.getAge().compareTo(p.getAge());
}
}
List<Person> list = new ArrayList<>();
list.add(new Person("Tom", 20));
list.add(new Person("Mike", 30));
list.add(new Person("John", 25));
Collections.sort(list, Person.AgeComparator);
System.out.println(list);
执行结果为:[Person [name=Tom, age=20], Person [name=John, age=25], Person [name=Mike, age=30]],可以看出,Person对象是按照年龄进行排序的。
Comparator
排序
Comparator和Comparable的排序方法是不同的,Comparable排序的方法是compareTo,而Comparator排序的方法是compare
List<String> list = new ArrayList<>();
list.add("add");
list.add("bean");
list.add("port");
Collections.sort(list,Comparator.comparing(String::length));
System.out.println(list);
当Collections的sort的方法,当传入Comparator类型变量时,则按照Comparator的方法排序
源码如下
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
自定义对象排序
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private int age;
public static Comparator<Person> AgeComparator = new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return p1.age - p2.age;
}
};
}
List<Person> list = new ArrayList<>();
list.add(new Person("Tom", 20));
list.add(new Person("Mike", 30));
list.add(new Person("John", 25));
Collections.sort(list, Person.AgeComparator);
System.out.println(list);
执行结果为:[Person [name=Tom, age=20], Person [name=John, age=25], Person [name=Mike, age=30]],可以看出,Person对象是按照年龄进行排序的。