zoukankan      html  css  js  c++  java
  • Java性能优化(11):考虑实现Comparable接口

    一个类实现了Comprable接口,就表明它的实例具有内在的排序关系。若一个数组中的对象实现了Comparable接口,则对这个数组进行排序非常简单:

    Arrays.sort(a);

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

    public class WordList
    {
        public static void main(String[] args)
        {
            Set s = new TreeSet();
            s.addAll(Arrays.asList(args));
            System.out.println(s);
        }
    }

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

    CompareTo方法的通用约定与equals方法的通用约定具有相似的特征,下面是它的内容。

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

    在下面的说明中,记号表示数学上的signum函数,它根据expresssion的值为负值、零和正值,分别返回-1、0或1.

    实现者必须保证对于所有的x和y,满足sgn(x.compareTo(y))=-sgn(y.compareTo(x))。

    • 实现者也必须保证这个比较关心是可传递的:(x.compareTo(y))>0&&y.compareTo(z)>0暗示着x.compareTo(z)>0。

    • 最后,实现者保证x.compareTo(y)==0 暗示着:对于所有的z,sgn(x.comrpareTo(z))==sgn(y.compareTo(z))。

    • 强烈建议(x.compareTo(y)==0)==(x.equals(y)),但这不是严格要求。一般而言,任何实现了Comparable接口的类,若违反了这个条件,应该明确予以说明。推荐使用这样的说法:”注意:该类具有内在排序功能,但是与equals不一致。”

    请不要对上述约定中的数学关系感到厌烦。如同equals约定一样,compareTo约定并没有它看起来那么复杂。在一个类的内部,任何一种合理的顺序关系都可以满足compareTo约定。然而,与equals不同的是,在跨越不同类的时候,compareTo可以不做比较:如果两个被比较对象引用分别指向不同类的对象,那么compareTo可以抛出ClassCastException异常。通常,这正是compareTo在这种情况下应该做的事情。

    就像一个违反了hashcode的约定的类会破坏其他的依赖于散列做法的类一样,一个违反了compareTo约定的类也会破坏其他依赖于比较关系的类。依赖于比较关系的类包括有序集合类TreeSet和TreeMap,以及工具类Collection和Arrays,它们内部包含有搜索和排序算法。

    域的本身是顺序比较,而不是相等比较。比较对象引用域可以通过递归地调用compareTo方法来实现。如果一个域斌没有实现Comparable接口,或者你需要使用一个域并没有实现Comparator。或者编写专门的Comparator。或者使用已有的Comparator,其compareTo方法使用一个已有的Comparator:

    public int compareTo(Object o)
    {
        CaseInsensitiveString cis = (CaseInsensitiveString)o;
        return String.CASE_INSENSITIVE_ODER.compare(s, cis.s);
    }

    比较原语类型的域,你可以使用关系操作符<和>;比较数组域是,你可以把这些指导原则应用到每一个元素上。如果一个类有多个关键域,那么,按什么样的顺序来比较这些域是非常关键的。你必须从最关键的域开始,逐步进行到所有的重要域。如果有一个域的比较产生了非零的结果,则整个比较操作结束,并返回该结果。如果最关键的域是相等的,则进一步比较次的最关键的域,以此类推。如果所有域都是相等的,则对象是相等的,返回零。

    public int compareTo(Object o) {
            PhoneNumber pn = (PhoneNumber) o;
            if (areaCode < pn.areaCode)
                return -1;
            if (areaCode > pn.areaCode)
                return 1;
            if (exchange < pn.exchange)
                return -1;
            if (exchange > pn.exchange)
                return 1;
            if (extension < pn.extension)
                return -1;
            if (extension > pn.extension)
                return 1;
            return 0;
        }

    虽然这个可以工作的很好,但它还有改进的余地。回想一下,compareTo方法的约定并没有指定返回值的大小,而是指定了返回值的符号。你可以利用这一点来简化代码,提高运行速度。

    public int compareTo(Object o) {
            PhoneNumber pn = (PhoneNumber) o;
            int areaCodeDiff=areaCode-pn.areaCode;
            if(areaCodeDiff!=0)
                return areaCodeDiff;
            int exchangeDiff=exchange-pn.exchange;
            if(exchangeDiff!=0)
                return exchangeDiff;
            return extension-pn.extension;
        }

    要谨慎使用这项技巧,除非你确信问题中的域不会是负的,或者更一般的情况,最小和最大的可能域值之差小于或者等于INTEGER.MAX_VALUE(2,-1),否则就不要使用这种方法。这项技巧往往不能正常工作的原因在于,一个有符号的32位整数还没有大到足以表达任意两个32位的差。如果i是一个很大的正整数,而j是一个绝对值很大的负整数,,那么(i-j)将会溢出,并返回一个负值。这就使得compareTo不能工作。对于某些实参,它会返回无意义的结果。

  • 相关阅读:
    CSS基础知识点
    HTML回顾
    stringbuffer与stringbuilder与String
    事务的概念与使用
    大数据处理对象CLOG BLOG
    小却常用的小知识点和技巧
    视图
    移位运算符
    规范化与范式
    聚集索引和非聚集索引
  • 原文地址:https://www.cnblogs.com/ainima/p/6331827.html
Copyright © 2011-2022 走看看