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

类实现了Comprable接口,就表明它的实例具有内在的排序关系。

为实现Comparable接口的对象数组进行排序就这么简单:
Arrays.sort(a);

对于存储在集合中的Comparable对象进行搜索,计算极限值以及自动维护工作都非常简单。

例如,下面的程序依赖于String实现了Comparable接口,它去掉了命令行参数列表中的重复参数,并按字母表顺序打印出来:

/**
 * @description: 单词列表
 * @author: lty
 * @create: 2021-05-17 11:01
 **/
public class WordList {
    public static void main(String[] args) {
        Set<String> s = new TreeSet<String>();
        Collections.addAll(s,args);
        System.out.println(s);
    }
}

一旦类实现了Comparable接口,它就可以跟许多泛型算法以及依赖于该接口的集合实现进行协作。你付出很小的努力就可以获得非常强大的功能。事实上,Java平台类库中的所有值类都实现了Comparable接口。如果你正在编写一个值类,它具有非常明显的内在排序关系,比如按字母顺序、按数值顺序或者按年代排序,那你就应该考虑实现这个接口:

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

一、compareTo方法的通用约定:

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

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

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

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

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

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

与equals不同的是,在跨域不同类的时候,compareTo可以不做比较:如果两个被比较的对象引用不同的类的对象,compareTo可以抛出ClassCastException异常。

无法在新的值组件扩展可实例化的类时,同时保持compareTo约定,除非愿意放弃面向对象的抽象优势。

针对于equals的权宜之计也同样适合compareTo方法。

如果你想为实现类Comparable接口的类增加值组件,请不要扩展这个类;而是要编写一个不相干的类,其中包含第一个类的一个实例。然后提供一个“视图(view)”方法返回这个实例。这样既可以让你自由的在第二个类上实现compareTo方法,同时也允许他的客户端在必要的时候,把第二个类的实例视同第一个类的实例。

二、如何编写compareTo方法:

编写compareTo方法与编写equals方法非常相似,但也存在几处重大的差别。因为Comparable接口是参数化的,而且comparable方法是静态的类型,因此不必进行类型检查,也不必对它的参数进行类型转。如果参数的类型不合适,这个调用甚至无法编译。如果参数为null,这个调用应该抛出NullPointException异常,并且一旦该方法试图访问它的成员时就应该抛出。

CompareTo方法中域的比较是顺序的比较,而不是等同性的比较。

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

public final class CaseInsensitiveString implements Comparable<CaseInsensitiveString>{
    public int compareTo(CaseInsensitiveString cis){
        return String.CASE_INSENSITIVE_ORDER.compare(s,cis.s)
    }
    ... //Remainder omitted
}

以上代码需注意:
CaseInsensitiveString类实现了Comparable接口。由此可见CaseInsensitiveString引用只能与其他的Comparable引用进行比较。在声明类去实现Comparable接口时,这是通常的模式。还是注意compareTo方法的参数是CaseInsensitiveString,而不是Object。这是上述的类声明所要求的。

比较整数类型基本类型的域,可以使用关系操作符 < 和 > 。
例如,浮点域用Double.compare或者Float.campare,而不是关系操作符,当应用到浮点值时,它们没有遵守compareTo的通用约定。对于数组域,则要把这些指导原则应用到每个元素上。
  
如果一个类有多个关键域,那么必须从最关键的域开始,逐步进行到所有的重要域。
如果某个域的比较产生了非零的结果(零代表相等),则整个比较操作结束,并返回该结果。如果最关键的域是相等的,则进一步比较次关键域,以此类推。如果所有的域都是相等的,则对象就是相等的,则返回零。

例如:第九条中的PhoneNumber类的compareTo方法。

public int compareTo(PhoneNumber pn){
    if (areaCode < pn.areaCode){
        return -1;
    }
    if (areaCode > pn.areaCode){
        return 1;
    }
    if (prefix < pn.prefix){
        return -1;
    }
    if (prefix > pn.prefix){
        return 1;
    }
    if (lineNumber < pn.lineNumber){
        return -1;
    }
    if (lineNumber > pn.lineNumber){
        return 1;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值