原因
对于一个有排序需求的类,建议实现Comparable
接口,因为这样可以让它轻松地被分类、搜索以及用在基于比较的集合中。
陈述
首先了解一下Comparable
:它是一个带泛型的接口,且只有一个方法compareTo
;
public interface Comparable<T> {
public int compareTo(T o);
}
当你实现了这个接口,重写了compareTo
方法,这个类便可以很方便地利用Java自带的方法进行排序操作,如Arrays.sort
方法。
Java中的所有值类
和枚举类
都实现了这个接口,值类
实现Comparable
接口的原因很简单,毕竟整天都在忙着比大小,作排序。而枚举类
的这个实现相对冷门,我们来一起看一下:
打开java.lang.Enum
,找到compareTo
方法
这里我们可以看到,首先是把两个枚举的class
进行了对比(这里不深究其对比逻辑),然后直接输出了ordinal
之差。
看一下ordinal
是什么:
说白了就是一个序号,这个序号怎么产生的呢,很简单,就是枚举在定义的时候顺序。
举个例子:
public enum Type {
A, // ordinal = 0
B, // ordinal = 1
C, // ordinal = 2
D; // ordinal = 3
}
所以Type.D.compareTo(Type.A)
的返回值就是3 - 0 = 3
。
虽然看起来有点意思,但是实际上有用吗?类的作者也考虑到了这一点,于是在注释上写了几笔:
简单来说就是,这玩意对绝大多数的码农来说没啥用,这个是设计给复杂的数据结构如EnumSet
和EnumMap
使用的。
规范
在重写compareTo
方法时,作者建议我们使用装箱类型自带的compare
方法来替代“>
”和“<
”,如:Double.compare
和Short.compare
,相对来说会更加简洁明朗。
另外书中作了一个引申,建议我们使用Compartor
接口,因为可以更方便的进行一些操作,拿之前博客里面提到的PhoneNum
举个例子:
public class PhoneNum implements Comparable<PhoneNum>{
/*区号*/
private Short areaCode;
/*号码*/
private Integer num;
/*分机号*/
private Integer extensionNum;
...
}
正常实现方式:
public int compareTo(PhoneNum other) {
int result = Short.compare(areaCode, other.areaCode);
if (result == 0) {
result = Integer.compare(num, other.num);
if (result == 0) {
result = Integer.compare(extensionNum, other.extensionNum);
}
}
return result;
}
用Compartor
实现:
// 先构造一个静态的 Comparator
private static final Comparator<PhoneNum> COMPARATOR = Comparator
.comparingInt((PhoneNum pn) -> pn.areaCode)
.thenComparingInt(pn -> pn.num)
.thenComparingInt(pn -> pn.extensionNum);
public int compareTo(PhoneNum other) {
return COMPARATOR.compare(this, other);
}
如此,代码的质量肉眼可见提升了。
总结
对于一个有排序需求的类,为了让它可以轻松地进行分类、搜索以和用在基于比较的集合中,建议实现Comparable
接口。在重写compareTo
时,记得使用装箱基本类型自带的compare
方法,对于逻辑较为复杂的逻辑,建议使用Comparator
。