下面分别对Comparable 和 Comparator做具体介绍并总结。
Comparable
Comparable可以认为是一个内比较器,实现了Comparable接口的类有一个特点,就是这些类是可以和自己比较的,至于具体和另一个实现了Comparable接口的类如何比较,则依赖compareTo方法的实现。
如果add进入一个Collection的对象想要Collections的sort方法帮你自动进行排序的话,那么这个对象必须实现Comparable接口。compareTo方法的返回值是int,有三种情况:
- 比较者大于被比较者,返回正整数
- 比较者等于被比较者,返回0
- 比较者小于被比较者,返回负整数
写个很简单的例子:
/**
* 实现Comparable重写compareTo方法进行Person的排序, 这里根据年龄排序
*
* @author
* @date
*/
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Person implements Comparable<Person> {
private String name;
private int age;
@Override
public int compareTo(Person p) {
// return p.getAge() - this.getAge()
return this.getAge() - p.getAge();
}
}
public class Demo {
public static void main(String[] args) {
Person p1 = new Person("aaa", 11);
Person p2 = new Person("bbb", 5);
Person p3 = new Person("cc", 8);
Person[] ps = {p1, p2, p3};
System.out.println(Arrays.toString(ps)); // 排序前
Arrays.sort(ps);
System.out.println(Arrays.toString(ps)); // 排序后
System.out.println("----------------");
Map<Person, String> map = new TreeMap<>();
map.put(p1, "aaa");
map.put(p2, "bbb");
map.put(p3, "ccc");
System.out.println(map.toString());
System.out.println("----------------");
List<Person> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
Collections.sort(list); // 排序
System.out.println(list.toString());
}
}
执行结果:
[Person(name=aaa, age=11), Person(name=bbb, age=5), Person(name=cc, age=8)]
[Person(name=bbb, age=5), Person(name=cc, age=8), Person(name=aaa, age=11)]
----------------
{Person(name=bbb, age=5)=bbb, Person(name=cc, age=8)=ccc, Person(name=aaa, age=11)=aaa}
----------------
[Person(name=bbb, age=5), Person(name=cc, age=8), Person(name=aaa, age=11)]
Comparator
Comparator接口里面有一个compare方法,方法有两个参数T o1和T o2,是泛型的表示方式,分别表示待比较的两个对象,方法返回值和Comparable接口一样是int,有三种情况:
- o1大于o2,返回正整数
- o1等于o2,返回0
- o1小于o3,返回负整数
写个很简单的例子:
public class ComparatorDemo {
public static void main(String[] args) {
/**
* 按照数组元素长度排序
*/
String[] str = {"abcdeftg", "maidi", "kebi", "zhanmusi"};
Arrays.sort(str, new Comparator<String>() {
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
System.out.println(Arrays.toString(str));
}
这里的Arrays.sort对数组进行排序用到了Compartor接口, 上面使用了匿名函数的写法, 也可以使用lambda进行改造...
Arrays.sort(str, (o1, o2) -> o1.length() - o2.length());
一行代码就可以搞定, 的确很简单
运行结果
[kebi, maidi, abcdeftg, zhanmusi]
因为泛型指定死了,所以实现Comparator接口的实现类只能是两个相同的对象(不能一个自定义对象、一个String)进行比较,实现Comparator接口的实现类一般都会以"待比较的实体类+Comparator"来命名, 只不过这里使用了匿名函数更简单而已
总结
上面的例子不管是Comparable还是Comparator, 都是按照升序来排序的, 如果想要按照反过来的顺序, 改改代码即可, 只需要修改接口中的实现方法, 调换一下相减的两个比较对象或者值即可, 例如"// return p.getAge() - this.getAge()"注释掉的代码那里一样
如果实现类没有实现Comparable接口,又想对两个类进行比较(或者实现类实现了Comparable接口,但是对compareTo方法内的比较算法不满意),那么可以实现Comparator接口,自定义一个比较器,写比较算法。
实现Comparable接口的方式比实现Comparator接口的耦合性要强一些,如果要修改比较算法,要修改Comparable接口的实现类,而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修改。因此:
- 对于一些普通的数据类型(比如 String, Integer, Double…),它们默认实现了Comparable 接口,实现了 compareTo 方法,我们可以直接使用。
- 而对于一些自定义类,它们可能在不同情况下需要实现不同的比较策略,我们可以新创建 Comparator 接口,然后使用特定的 Comparator 实现进行比较。
不同之处:
实现Comparable接口的方式比实现Comparator接口的耦合性要强