• Java


    类实现了Comparable接口就表明类的实例本身具有内在的排序关系(natural ordering)。
    因此,该类可以与很多泛型算法和集合实现进行协作。
    而我们之需要实现Comparable接口唯一的方法——compareTo


    以下是相关规则:

    • sgn(x.compareTo(y)) = -sgn(y.compareTo(x))
    • (x.compareTo(y)>0 && y.compareTo(z)>0) 则 x.compareTo(z)>0
    • x.compareTo(y) == 0 则 sgn(x.compareTo(z)) == sgn(y.compareTo(z))
    • 建议x.compareTo(y) == 0 时 x.equals(y)


    第四条并不是必须的,但值得了解一下。
    一些有序结构中的等同性比较可能会使用compareTo而非equals。
    鉴于这种情况,我们需要把compareTo和equals兼容起来。
    但特殊情况不必这样,比如BigDecimal类中new BigDecimal("1.00")new BigDecimal("1.0"),两者equals结果为false,compareTo结果为0。


    对于类中不同类型的field进行不同的比较方法:

    • 非浮点基本类型直接使用关系操作符
    • 浮点类型使用封装类的compare方法
    • 引用类型可以递归调用compareTo,如果发现某个field没有实现Comparable,则提供显示的Comparator。
    • 数组类型则把上述规则应用到每一个元素。


    通常情况下,对于一个类的关键field,我们可以根据它们的关键程度做一个优先级。
    从最关键的开始逐个比较,得出非零结果时立即返回。


    下面是例子:

    // Making PhoneNumber comparable - Pages 65-66
    package org.effectivejava.examples.chapter03.item12;
    
    import java.util.NavigableSet;
    import java.util.Random;
    import java.util.TreeSet;
    
    public final class PhoneNumber implements Cloneable, Comparable<PhoneNumber> {
        private final short areaCode;
        private final short prefix;
        private final short lineNumber;
    
        public PhoneNumber(int areaCode, int prefix, int lineNumber) {
            rangeCheck(areaCode, 999, "area code");
            rangeCheck(prefix, 999, "prefix");
            rangeCheck(lineNumber, 9999, "line number");
            this.areaCode = (short) areaCode;
            this.prefix = (short) prefix;
            this.lineNumber = (short) lineNumber;
        }
    
        private static void rangeCheck(int arg, int max, String name) {
            if (arg < 0 || arg > max)
                throw new IllegalArgumentException(name + ": " + arg);
        }
    
        @Override
        public boolean equals(Object o) {
            if (o == this)
                return true;
            if (!(o instanceof PhoneNumber))
                return false;
            PhoneNumber pn = (PhoneNumber) o;
            return pn.lineNumber == lineNumber && pn.prefix == prefix
                    && pn.areaCode == areaCode;
        }
    
        @Override
        public int hashCode() {
            int result = 17;
            result = 31 * result + areaCode;
            result = 31 * result + prefix;
            result = 31 * result + lineNumber;
            return result;
        }
    
    
    
        @Override
        public PhoneNumber clone() {
            try {
                return (PhoneNumber) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new AssertionError(); // Can't happen
            }
        }
    
        // Works fine, but can be made faster
        // public int compareTo(PhoneNumber pn) {
        // // Compare area codes
        // if (areaCode < pn.areaCode)
        // return -1;
        // if (areaCode > pn.areaCode)
        // return 1;
        //
        // // Area codes are equal, compare prefixes
        // if (prefix < pn.prefix)
        // return -1;
        // if (prefix > pn.prefix)
        // return 1;
        //
        // // Area codes and prefixes are equal, compare line numbers
        // if (lineNumber < pn.lineNumber)
        // return -1;
        // if (lineNumber > pn.lineNumber)
        // return 1;
        //
        // return 0; // All fields are equal
        // }
    
        public int compareTo(PhoneNumber pn) {
            // Compare area codes
            int areaCodeDiff = areaCode - pn.areaCode;
            if (areaCodeDiff != 0)
                return areaCodeDiff;
    
            // Area codes are equal, compare prefixes
            int prefixDiff = prefix - pn.prefix;
            if (prefixDiff != 0)
                return prefixDiff;
    
            // Area codes and prefixes are equal, compare line numbers
            return lineNumber - pn.lineNumber;
        }
    
        public static void main(String[] args) {
            NavigableSet<PhoneNumber> s = new TreeSet<PhoneNumber>();
            for (int i = 0; i < 10; i++)
                s.add(randomPhoneNumber());
            System.out.println(s);
        }
    
        private static final Random rnd = new Random();
    
        private static PhoneNumber randomPhoneNumber() {
            return new PhoneNumber((short) rnd.nextInt(1000),
                    (short) rnd.nextInt(1000), (short) rnd.nextInt(10000));
        }
    }
    


    上面的例子中的field都是非浮点基本类型,于是作者对其进行优化。
    鉴于compareTo只需要返回值的符号而非大小,因此用差值代替逻辑比较符。


    但是这种用法需要注意,该field的类型可能无法容纳两个数值的差值。
    比如Integer.MAX_VALUE-(-1)或者Integer.MIN_VALUE-1之类的。
    如果可以保证field值不会是负值,则不会出现这种情况。

  • 相关阅读:
    机器人对话小程序
    partial关键字的含义
    TreeView控件常用写法
    电话本管理程序(实现增删改查功能)
    三层架构
    c# RegistryKey 的相关简单操作
    VS2010程序打包操作(超详细的)
    一些中文.net讲座
    对象表单自动数据邦定
    AspNetForums 2.0中的全文检索
  • 原文地址:https://www.cnblogs.com/kavlez/p/4194477.html
Copyright © 2020-2023  润新知