Comparable
接口是一个泛型接口,代码如下:
public interface Comparable<T> { int compareTo(T t); }
类实现 Comparable
接口,就表明它的实例具有内在的排序关系,比如按照字母顺序、按数值顺序或者按年代顺序,那你就应该考虑实现 Comparable
接口:
一般来说,值类适合实现 Comparable
接口。
将一个对象与另一个对象进行比较时。当对象小于、等于或大于指定对象的时候,分别返回一个负整数、零或正整数。如果由于指定对象的类型而无法与该对象进行比较,则抛出 ClassCastException
异常。
实现 Comparable 接口的类,在实现时应该满足以下约定(注意,在下面的说明中,符号 sgn(...) 表示数学中的 signum 函数,它根据表达式的值为负、零、正,分别返回-1,0,1):
sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
(交换律)(x.compareTo(y) > 0 && y.compareTo(z) > 0)
则 x.comareTo(z) > 0
;(传递律)x.compareTo(y) == 0
则对于所有的 z
都满足 sgn(x.compareTo(z)) == sgn(y.compareTo(z))
(x.compareTo(y) == 0) == (x.equals(y))
。但也并非绝对必要,如果违反这一条件,都应该在文档中明确予以说明。注意,Comparable
接口不能跨越不同类型进行比较,在比较不同类型的对象时,可以在 compareTo
函数中抛出 ClassCastException
异常来拒绝执行。
违反 Comparable
接口约定会破坏其他依赖于比较关系的类或方法,如 TreeSet
、TreeMap
、工具类 Collections
,Arrays
,因为它们内部都用与排序相关的算法。
compareTo
方法中使用关系操作符>
或<
去比较基本类型是十分繁琐的,或者使用-
加>0
判断。对于基本类型在 compareTo
函 数中进行比较应该使用静态方法代替,如Integer.compare(a, b)
、Float.compare(a, b)
、Double.compare(a, b)
等等。// 太繁琐 public int compare(Integer A, Integer B) { if A > B { return 1; } else if A < B { return -1; } else { return 0; } } // 有溢出风险 public int compare(Integer A, Integer B) { return A - B; } // 最佳 public int compare(Integer A, Integer B) { return Integer.compare(A, B); }
Comparator
接口配置了一组比较构造器方法,使得比较器的构造工作变得更加流畅。之后,按照 Comparable
接口的要求,这些比较器可以用来实现一个 compareTo
方法。如下:private static class PhoneNumber implements Comparable<PhoneNumber>{ private final short areaCode; private final short prefix; private final short lineNum; public PhoneNumber(short areaCode, short prefix, short lineNum) { this.areaCode = areaCode; this.prefix = prefix; this.lineNum = lineNum; } // 使用 Comparator 可以轻松实现对关键域按优先级进行比较 private static final Comparator<PhoneNumber> COMPARATOR = Comparator .comparingInt((ToIntFunction<PhoneNumber>) value -> value.areaCode) .thenComparingInt(value -> value.prefix) .thenComparingInt(value -> value.lineNum); @Override public int compareTo(PhoneNumber o) { return COMPARATOR.compare(this, o); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PhoneNumber that = (PhoneNumber) o; return areaCode == that.areaCode && prefix == that.prefix && lineNum == that.lineNum; } @Override public int hashCode() { return Objects.hash(areaCode, prefix, lineNum); } }
compareTo
函数中避免使用 > 或 < 操作符,不要通过减法运算去进行判断,有可能溢出compareTo
函数编写起来比较复杂,比如关键域较多的情况,应该单独实现一个静态Comparator
比较构造器,并通过它简介实现 comparaTo
接口。