第十四条:考虑实现Comparable接口

compareTo 方法并没有在 Object 类中声明 。 相反,它是 Comparable 接口中唯一 的方法 。
compareTo 方法不但允许进行简单的等同性比较,而且允许执行顺序比较,除此之外 ,它与 Object 的 equals 方法具有相似的特征,它还是个泛型( generic ) 。 类实现了 Comparable 接口,就表明它的实例具有 内在的排序关系(natural ordering ) 。 为实现 Comparable 接口的对象数组进行排序就这么简单 :

Arrays.sort(a);


        对存储在集合中的 Comparable 对象进行搜索、计算极限值以及自动维护也同样简单 。 例如,下面的程序依赖于实现了 Comparable 接口的 String 类,它去掉了命令行参数列表中的重复参数,并按字母顺序打印出来:

public class TestDemo {
    public static void main(String[] args) {


        Set<String> s = new TreeSet<>() ;
        s.add("a");
        s.add("n");
        s.add("b");
        Collections.addAll(s,args);
        System.out.println(s);

    }
}

结果为:

[a, b, n]

Process finished with exit code 0

compareTo方法的通用约定与equals方法相似:

  将这个对象与指定对象进行比较。当该对象小于、等于或大于指定对象的时候,分别返回一个负整数、零或者正整数。如果由于指定对象的类型而无法与该对象进行比较,则抛出ClassCastException异常。

  在下面的说明中,符号sgn(表达式)表示数学中的signum函数,它根据表达式(expression)的值为负值、零和正值分别返回-1、0或1。

1.实现者必须确保所有的x和y都满足sgn(x.compareTo(y)) == -sgn(y.compareTo(x))。(这也暗示着,当且仅当y.compareTo(x)抛出异常时,x.compareTo(y)才必须抛出异常。)

2.实现者还必须确保这个比较关系是可传递的:(x.compareTo(y) > 0 && y.compareTo(z) > 0)暗示着x.compareTo(z) > 0。

3.最后,实现者必须确保x.compareTo(y) == 0暗示着所有的z都满足sgn(x.compareTo(z)) == sgn(y.compareTo(z))。

4.强烈建议(x.compareTo(y) == 0) == (x.equals(y)),但这并非绝对必要。一般来说,任何实现了Compareable接口的类,若违反了这个条件,都应该明确予以说明。推荐使用这样的说法:“注意:该类具有自然的排序功能,但是与equals不一致”

 CompareTo 方法中域的比较是顺序的比较,而不是等同性的比较 。 比较对象引用域可以通过递归地调用 compareTo 方法来实现 。 如果一个域并没有实现 Comparable 接口,或者你需要使用一个非标准的排序关系,就可以使用一个显式的 Comparator 来代替 。 或者编写自己的比较器,或者使用已有的比较器例。

在 compare To 方法中使用关系操作符 < 和 > 是非常烦琐的,并且容易出错,因此不再建议使用 。

在Java 8中,Comparator接口配备了一组比较器构造方法,可以精确构建比较器。然后,可以使用这些比较器来实现compareTo方法,这是Comparable接口所要求的。许多程序猿更喜欢这种方法的简洁性,尽管它的性能成本很低:在我的机器上排序PhoneNumber实例的数组大约慢10%。使用这种方法时,考虑使用Java的静态导入功能,这样您就可以通过简单的名称来引用静态比较器构造方法,以获得清晰和简洁【代码】。这就是PhoneNumber的compareTo方法看起来是如何使用这种方法:

public final class PhoneNumber {

    private final int areaCode, prefix, lineNum;


    public PhoneNumber(int areaCode, int prefix, int lineNum) {
        this.areaCode = areaCode;
        this.prefix = prefix;
        this.lineNum = lineNum;
    }

    private static final Comparator<PhoneNumber> COMPATOR = comparingInt((PhoneNumber pn) -> pn.areaCode)
            .thenComparingInt(pn -> pn.prefix)
            .thenComparingInt(pn -> pn.lineNum);


    public int compareTo(PhoneNumber o) {
        return COMPATOR.compare(this, o);
    }
}

偶尔也有人使用两个值的差来实现compareTo或者compare方法:如第一个值小于第二个值,那么差是负数;如果两个值相等,差就是零;如果第一个值大于第二个值,那么差就是正数。下面就是例子:

// 存在问题,基于差的比较器,会破坏传递性
static Comparator<Object> hashCodeOrder = new Comparator<>() {
    public int compare(Object o1, Object o2) {
        return o1.hashCode() - o2.hashCode();
    }
};

不要使用这种技巧。它充满了整数溢出和IEEE 754浮点运算伪像的危险[JLS 15.20.1,15.21.1](It is fraught with danger from integer overflow and IEEE 754 floating point arithmetic artifacts [JLS 15.20.1, 15.21.1])。此外,所得到的方法不太可能比使用项中描述的技术编写的方法快得多。使用静态比较方法:

// 基于静态的compare方法的比较器
static Comparator<Object> hashCodeOrder = new Comparator<>() {
    public int compare(Object o1, Object o2) {
        return Integer.compare(o1.hashCode(), o2.hashCode());
    }
};

 或者一个比较器的构造方法:

// 基于比较器方法的构造器
static Comparator<Object> hashCodeOrder = Comparator.comparingInt(o -> o.hashCode());

 总之,无论何时实现具有合理排序的值类,都应该让类实现Comparable接口,以便可以在基于比较的集合中轻松地对其实例进行排序,搜索和使用。compareTo方法的实现中在比较字段的值的时侯,请避免使用 < 和 > 运算符。 而是使用基本类型的包装类中的静态比较方法或比较器接口中的比较器构造方法

所有文章无条件开放,顺手点个赞不为过吧!

                                       

  • 24
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值