Comparable和Comparator
- Comparable和Comparator是什么?
- 比较规则
- 用法
- 额外的比较器
- 该使用哪个?
- 比较与继承
Comparable和Comparator是什么?
- Comparable为类提供了默认比较
- Comparator可以为类提供额外的比较方式
比较规则
对于 int compareTo() 和 int compare() 方法
- 顺序排序:返回值 > 0
- 逆序排序:返回值 < 0
- 不排序:返回值 = 0
- 返回值通常利用±1和0
重写要保证对于所有的x、y、z(sgn表示根据表达式的值返回-1、0、1)
- 自反性:sgn[ x.compareTo(y) ] == -sgn[ y.compareTo(x) ]
- 传递性:x.compareTo(y) > 0 && y.compareTo(z) > 0 && x.compareTo(z) > 0
- 对称性:x.compareTo(y) == 0 && sgn[ x.compareTo(z) ] == sgn[ y.compareTo(z) ]
- 此外建议满足 [ x.compareTo(y) == 0 ] == [ x.equals(y) ]
用法
比较应该从最关键的域开始,逐步进行到所有的域,若某个域的比较产生了非零的结果,则整个比较结束并返回该结果,如下
class Person implements Comparable<Person> {
private String name;
private int age;
private int height;
@Override
public int compareTo(Person o) {
int result = name.compareTo(o.name);
if (result == 0) {
result = Integer.compare(this.age, o.age);
if (result == 0) {
result = Integer.compare(this.height, o.height);
}
}
return result;
}
}
java8后Comparator提供了比较器构造方法,可简化上述比较过程
class Person implements Comparable<Person> {
private String name;
private int age;
private int height;
private static final Comparator<Person> COMPARATOR =
Comparator.comparing((Person p) -> p.name)
.thenComparingInt(p -> p.age)
.thenComparingInt(p -> p.height);
@Override
public int compareTo(Person o) {
return COMPARATOR.compare(this, o);
}
}
Tips:
- 继承Comparable应指定泛型限制比较对象及省去类型转换
- 比较不建议使用 < 和 >
- 对于基本数据类型,调用其包装类的compare方法
- 对于引用数据类型,应递归调用其compareTo方法
- Comparator的比较器构造方法是向下兼容的,如对于short也是用thenComparingInt
额外的比较器
如默认比较方式不符合要求,可通过Comparator定义额外的比较方式
class Person{
int age;
long height;
}
定义两个比较器,分别根据Age和Height进行比较,当需要比较时按需创建
class compareByAge implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return Integer.compare(o1.age, o2.age);
}
}
class compareByHeight implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return Long.compare(o1.height, o2.height);
}
}
该使用哪个?
- 若是类库设计者或只有一种比较方式,应该为Bean提供默认比较(即实现Comparable),方便使用者直接使用
- 若默认比较无法满足需求或要修改比较域,应该额外实现Comparator
- 若存在多种比较方式,应该实现Comparator以创建不同的比较器
比较与继承
同equals()一样,compareTo()无法在继承时增加比较域又保持性质,而解决办法是把继承改为组合,并在子类中提供一个返回父类的公有视图(修改后将不再允许父类和子类比较)
class Person implements Comparable<Person> {
private int age;
public Person(int age) {
this.age = age;
}
@Override
public int compareTo(Person o) {
return Integer.compare(this.age, o.age);
}
}
class Man implements Comparable<Man> {
private Person person;
private int height;
public Man(int age, int height) {
person = new Person(age);
this.height = height;
}
public Person asPerson() {
return person;
}
@Override
public int compareTo(Man o) {
int result = this.person.compareTo(o.person);
if (result == 0) {
result = Integer.compare(this.height, o.height);
}
return result;
}
}
其实比较在继承中是比较麻烦的,根据情况不同,需要判断传进来的是父类还是子类,比较的是父类的域还是子类新增的域,其中可能还涉及到多重继承,为避免这些复杂的情况,推荐使用上述的方式一劳永逸
如下记录几种别的方式(以后研究)
//使用父类域比较不重写compareTo
/*class Man extends Person{
private int height;
public Man(int age, int height) {
super(age);
this.height = height;
}
}*/
//当传递父类时用父类域,传递子类时用父类+子类域
/*class Man extends Person implements Comparable<Man> {
private int height;
public Man(int age, int height) {
super(age);
this.height = height;
}
@Override
public int compareTo(Person o) {
return super.compareTo(o);
}
@Override
public int compareTo(Man o) {
int result = super.compareTo(o);
if (result == 0) {
result = Integer.compare(this.height, ((Man) o).height);
}
return result;
}
}*/
//同上,不过没实现Comparable
/*class Man extends Person {
private int height;
public Man(int age, int height) {
super(age);
this.height = height;
}
@Override
public int compareTo(Person o) {
if (o.getClass() == Person.class) {
return super.compareTo(o);
}
int result = super.compareTo(o);
if (result == 0) {
result = Integer.compare(this.height, ((Man) o).height);
}
return result;
}
}*/