compareTo 方法并没有在 Object 类中声明 。 相反,它是 Comparable 接口中唯一 的方法 。
compareTo 方法不但允许进行简单的等同性比较,而且允许执行顺序比较,除此之外 ,它与 Object 的 equals 方法具有相似的特征,它还是个泛型( generic ) 。 类实现了 Comparable 接口,就表明它的实例具有 内在的排序关系(natural ordering ) 。 为实现 Comparable 接口的对象数组进行排序就这么简单 :
Arrays.sort(a);
对存储在集合中的 Comparable 对象进行搜索、计算极限值以及自动维护也同样简单 。 例如,下面的程序依赖于实现了 Comparable 接口的 String 类,它去掉了命令行参数列表中的重复参数,并按字母顺序打印出来:
public class WordList {
public static void main(String[] args) {
Set<String> S = new TreeSet<>() ;
Collections.addAll(s,args);
System.out.println(s);
}
}
一旦类实现了 Comparable 接口,它就可以跟许多泛型算法( generic algorithm )以及依赖于该接口的集合实现( collection implementation )进行协作 。 你付出很小的努力就可以获得非常强大的功能。事实上,Java 平台类库中 的所有值类( value classes ),以及所有的枚举类型都实现了 Comparable 接口 。 如果你正在编写一个值类,它具有非常明显的内在排序关系,比如按字母顺序、按数值顺序或者按年代顺序,那你就应该坚决考虑实现 Comparable 接口:
public interface Comparable<T> {
int compareTo(T t);
}
compareTo 方法的通用约定与 equals 方法的约定相似 :
将这个对象与指定的对象进行比较 。 当该对象小于、等于或大于指定对象的时候,分别返回一个负整数、零或者正整数 。 如果由于指定对象的类型而无法与该对象进行比较,则抛出 ClassCastException 异常 。
compareTo 约定并没有看起来那么复杂 。 与 equals 方法不同的是,它对所有的对象强行施加了一种通用的等同关系,compareTo 不能跨越不同类型的对象进行比较:在比较不同类型的对象时,compareTo 可以抛出 ClassCastException 异常 。 通常,这正是compareTo 在这种情况下应该做的事情 。 合约确实允许进行跨类型之间的比较,这一般是在被比较对象实现的接口中进行定义 。
编写 compareTo 方法与编写 equals 方法非常相似,但也存在几处重大的差别 。因为 Comparable 接口是参数化的,而且 comparable 方法是静态的类型,因此不必进行类型检查,也不必对它的参数进行类型转换 。 如果参数的类型不合适,这个调用甚至无法编译 。 如果参数为 null ,这个调用应该抛出 NullPointerException 异常,并且一旦该方法试图访问它的成员时就应该抛出异常 。
CompareTo 方法中域的比较是顺序的比较,而不是等同性的比较 。 比较对象引用域可以通过递归地调用 compareTo 方法来实现 。 如果一个域并没有实现 Comparable 接口,或者你需要使用一个非标准的排序关系,就可以使用一个显式的 Comparator 来代替 。 或者编写自己的比较器,或者使用已有的比较器例。
在 compare To 方法中使用关系操作符 < 和 > 是非常烦琐的,并且容易出错,因此不再建议使用 。
如果一个类有多个关键域 ,那么 , 按什么样的顺序来比较这些域是非常关键的 。 你必须从最关键的域开始,逐步进行到所有的重要域 。 如果某个域的比较产生 了非零的结果(零代表相等),则整个比较操作结束,并返回该结果。 如果最关键的域是相等的,则进一步比较次关键的域,以此类推 。 如果所有的域都是相等的 , 则对象就是相等的 ,并返回零 。
总而言之,每当实现一个对排序敏感的类时,都应该让这个类实现 Comparable 接口,以便其实例可以轻松地被分类 、搜索,以及用在基于比较的集合中 。 每当在 compareTo 方法的实现中比较域值时,都要避免使用 < 和>操作符,而应该在装箱基本类型的类中使用静态的 compare 方法,或者在 Comparator 接口中使用比较器构造方法 。