1 Comparable 接口
Comparable接口是排序接口,只能是类来实现Comparable接口。既然实现Comparable接口的类支持排序,那么“ 实现了Comparable接口的类的对象的List列表(或数组)”,可以通过Collections.sort(或Arrays.sort)进行排序。
“实现Comparable接口的类的对象”可以用作“有序映射(如TreeMap)”中的键或“有序集合(TreeSet)”中的元素,而不需要指定比较器。
Comparable接口定义
public interface Comparable<T> {
public int compareTo(T o);
}
- compareTo() 方法用于两种方式的比较:
- 字符串与对象进行比较
- 按字典顺序比较两个字符串
- 如果想进行两个数字之间的比较,必须是两个包装类之间的比较。可以是一个 Byte, Double, Integer, Float, Long 或 Short 类型的参数。
示例代码
- Person类定义
/**
* Person类实现Comparable接口,使该类具有排序的功能
* @author jie
* @date 2021-07-06 10:44
*/
public class Person implements Comparable<Person>{
private int age;
private String name;
/**
* 实现 “Comparable<String>” 的接口,即重写compareTo<T t>函数。
* 这里是通过“person的名字”进行比较的
*/
@Override
public int compareTo(Person person) {
return name.compareTo(person.name);
}
...
}
注意:
- 实现的Comparable接口的泛型必须是待排序的类
- compareTo()传入的参数是待排序类的对象
- compareTo() 不能直接对int类型的age进行排序,如果想使用compareTo()进行排序,必须将int类型的age转成Integer类型的age
- 在main()中,我们创建了Person的List数组(list)
List<Person> list = new ArrayList<>();
// 添加对象到ArrayList中
list.add(new Person("ccc", 20));
list.add(new Person("AAA", 30));
list.add(new Person("bbb", 10));
list.add(new Person("ddd", 40));
- 打印list里的全部元素
// 打印list的原始序列
System.out.printf("Original sort, list:%s\n", list);
/**
运行结果:
Original sort, list:[Person{age=20, name='ccc'}, Person{age=30, name='AAA'}, Person{age=10, name='bbb'}, Person{age=40, name='ddd'}]
*/
- 通过Collections的sort()函数,对list进行排序
正如前面所说,实现了Comparable接口的类的对象的List列表(或数组)”,可以通过Collections.sort(或Arrays.sort)进行排序。
由于Person实现了Comparable接口,因此通过sort()排序时,会根据Person支持的排序方式,即 compareTo(Person person) 所定义的规则进行排序。如下:
// 对list进行排序
// 这里会根据“Person实现的Comparable<String>接口”进行排序,即会根据“name”进行排序
Collections.sort(list);
System.out.printf("Name sort, list:%s\n", list);
/**
运行结果:
Name sort, list:[Person{age=30, name='AAA'}, Person{age=10, name='bbb'}, Person{age=20, name='ccc'}, Person{age=40, name='ddd'}]
*/
2 Comparator 接口
Comparator 是比较器接口。
我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);那么,我们可以建立一个“该类的比较器”来进行排序。这个“比较器”只需要实现Comparator接口即可。
也就是说,我们可以通过“实现Comparator类来新建一个比较器”,然后通过该比较器对类进行排序。
Comparator接口定义
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
示例代码
- Person类定义
① 实现Comparable接口
/**
* 实现Comparable接口,使该类具有排序的功能
* @author jie
* @date 2021-07-06 14:49
*/
public class PersonComparable implements Comparable<PersonComparable> {
int age;
String name;
/**
* 实现 “Comparable<String>” 的接口,即重写compareTo<T t>函数。
* 这里是通过“personComparable的name属性”进行比较的
*/
@Override
public int compareTo(PersonComparable personComparable) {
return name.compareTo(personComparable.name);
}
...
}
② 未实现Comparable接口
public class Person implements Comparable<Person>{
private int age;
private String name;
}
- 在main()中,创建Person的List数组(list)
① 实现了Comparable接口
List<PersonComparable> list = new ArrayList<>();
// 添加对象到ArrayList中
list.add(new PersonComparable("ccc", 20));
list.add(new PersonComparable("AAA", 30));
list.add(new PersonComparable("bbb", 10));
list.add(new PersonComparable("ddd", 40));
② 未实现Comparable接口
List<Person> list = new ArrayList<>();
// 添加对象到ArrayList中
list.add(new Person("ccc", 20));
list.add(new Person("AAA", 30));
list.add(new Person("bbb", 10));
list.add(new Person("ddd", 40));
- 利用Comparator比较器对列表进行排序
Collections.sort()定义
// 使用lambda表达式实现Comparator接口,即自定义排序规则
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
Comparator接口里的int compare(T o1, T o2)
-
当比较的两个参数是对象时,需要对象实现Comparable接口
-
当比较的两个参数是字符串时,需要使用
compareTo()
来比较 -
当比较的两个参数是数值时,直接使用
减号 -
来比较
比较的两个参数是对象
① 实现了Comparable接口
Collections.sort(list, (o1, o2)->{
// 这里o1, o2表示的是list列表里单个元素表示的对象,即PersonComparable类的对象
return o1.compareTo(o2);
});
System.out.println("排序后:"+list);
/**
运行结果:
排序后:[PersonComparable{age=30, name='AAA'}, PersonComparable{age=10, name='bbb'}, PersonComparable{age=20, name='ccc'}, PersonComparable{age=40, name='ddd'}]
*/
等价于 Collections.sort(list),正好对应了前面所说的。
② 未实现Comparable接口
程序直接报错,编译都过不了。
总结:
- 实现了Comparable接口的类,可以通过Comparator比较器来进行排序,排序的规则是类中重写的compareTo()方法中自定义的(这里是按照name排序)
- 未实现Comparable接口的类,无法通过Comparator比较器来进行排序。
比较的两个参数是字符串或包装类
① 未实现Comparable接口
Collections.sort(list, (o1, o2)->{
// 根据name排序,这里o1, o2表示的是list列表里单个元素表示的对象
return o1.name.compareTo(o2.name);
});
System.out.println("name排序后:" + list);
/**
运行结果:
name排序后:[Person{age=30, name='AAA'}, Person{age=10, name='bbb'}, Person{age=20, name='ccc'}, Person{age=40, name='ddd'}]
*/
② 实现Comparable接口
和未实现Comparable接口的结果一样,能按照name正常排序。
比较的两个参数是数值
① 未实现Comparable接口
Collections.sort(list, (o1, o2)->{
// 根据age排序
return o1.age - o2.age;
});
System.out.println("age排序后:" + list);
/**
运行结果:
age排序后:[Person{age=10, name='bbb'}, Person{age=20, name='ccc'}, Person{age=30, name='AAA'}, Person{age=40, name='ddd'}]
*/
② 实现Comparable接口
Collections.sort(list, (o1, o2)->{
return o1.age - o2.age;
});
System.out.println("排序后:"+list);
/**
运行结果:
排序后:[PersonComparable{age=10, name='bbb'}, PersonComparable{age=20, name='ccc'}, PersonComparable{age=30, name='AAA'}, PersonComparable{age=40, name='ddd'}]
*/
会覆盖Comparable接口中compareTo()方法中自定义的排序规则, 和未实现Comparable接口的结果一样,能按照age正常排序。
数组Arrays工具类同理
Arrays.sort(persons, (o1, o2)->{
// 根据name排序,这里o1, o2表示的是list列表里单个元素表示的对象
return o1.name.compareTo(o2.name);
});
Arrays.sort(persons, (o1, o2)->{
// 根据age排序
return o1.age - o2.age;
});
3 总结
Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”。
而Comparator是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
我们不难发现:Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
- Comparable接口,只是为了将类可以按照自定义的排序规则排序,在重写的compareTo()方法中,字符串类型的参数可以直接比较,而数值类型的必须使用对应的包装类进行比较。
- “ 实现了Comparable接口的类的对象的List列表(或数组)”,可以通过Collections.sort(或Arrays.sort)进行排序。相当于是Comparator接口中重写的compare()方法,比较的是两个对象。
- Comparator接口中重写的compare()方法,当比较的两个参数是字符串或包装类时,需要使用
compareTo()
来比较 - Comparator接口中重写的compare()方法,当比较的两个参数是数值时,直接使用
减号 -
来比较
参考资料: