ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

【Effective Java 14】考虑实现 Comparable 接口

2022-04-13 21:35:39  阅读:156  来源: 互联网

标签:Comparable return 14 Integer 接口 prefix compareTo Java


1. 什么时候应该让类实现 Comparable 接口

Comparable 接口是一个泛型接口,代码如下:

public interface Comparable<T> {
    int compareTo(T t);
}

类实现 Comparable 接口,就表明它的实例具有内在的排序关系,比如按照字母顺序、按数值顺序或者按年代顺序,那你就应该考虑实现 Comparable 接口:

一般来说,值类适合实现 Comparable 接口。

2. 使用 Comparable 接口的约定

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

实现 Comparable 接口的类,在实现时应该满足以下约定(注意,在下面的说明中,符号 sgn(...) 表示数学中的 signum 函数,它根据表达式的值为负、零、正,分别返回-1,0,1):

  • 实现者必须确保所有的 x 和 y 都满足 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 接口约定会破坏其他依赖于比较关系的类或方法,如 TreeSetTreeMap、工具类 CollectionsArrays,因为它们内部都用与排序相关的算法。

3. 使用 Comparable 接口的建议

  • 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);
}
  • 如果一个类有多个关键域,按照什么样的顺序来比较这些域是十分关键的。你必须从最关键的域开始,逐步进行到所有重要的域。

4. 使用 Comparator 接口

  • 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);
    }
}

5. 总结

  • 对于排序敏感类,都应该让该类实现 Comparable 接口
  • compareTo 函数中避免使用 > 或 < 操作符,不要通过减法运算去进行判断,有可能溢出
  • 如果 compareTo 函数编写起来比较复杂,比如关键域较多的情况,应该单独实现一个静态Comparator 比较构造器,并通过它简介实现 comparaTo 接口。

标签:Comparable,return,14,Integer,接口,prefix,compareTo,Java
来源: https://www.cnblogs.com/Silgm/p/16142123.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有