一、Comparable(内部排序接口)

实现该接口的类,支持自然排序(内排序)。Arrays.sort(Object[])和Collection.sort(Object[])要求对象必须实现Comparable接口

文档中指出:

  1. 如果该对象大于指定对象,返回正整数
  2. 如果该对象等于指定对象,返回0
  3. 如果该对象小于指定对象,返回负整数
  4. 建议(x.compareTo(y)==0) == x.equals(y)
  5. e.compareTo(null) 应该抛出NullPointerException
  6. 键a和b被添加到没有指定显示比较器的有序集合中。if(!a.equals(b) && a.compareTo(b))(eqauls方法与compareTo方法不一致)。在有序集合看来a和b是相等的,因此第2个键无法被添加到集合中。

针对第6点可以举个例子:
BigDecimal类的compareTo 与 equals 方法不一致。

public static void main(String[] args) {
        Map<BigDecimal,String> map = new HashMap<BigDecimal,String>();
        BigDecimal b1 = new BigDecimal(1.0);
        BigDecimal b2 = new BigDecimal(1.00);
        map.put(b1,"c1");
        map.put(b2,"c2");
        System.out.println(map.get(b1) + " " + map.get(b2)); // 输出 c1、c2
        TreeSet<BigDecimal> set = new TreeSet<BigDecimal>();
        set.add(b1);
        set.add(b2);
        for(BigDecimal b : set) {
            System.out.println(b.floatValue()); // 只打印出1.0
        }
    }

HashMap通过 hashCode + equals 方法进行比较。 TreeSet 通过 compareTo进行比较。

在effctiveJava中 指出,Comparable接口,具有与equals方法一样的自反、传递、对称。所以同样与equals具有同样的特征:
没有一种简单的方法可以做到,在扩展一个新的可实例化的类时,既增加了新的特征,又保持了compareTo的约定(见我的文章

对于如何编写compareTo方法,effectivejava中,同样给出了以下建议。并且推荐,自定义的类,可以考虑实现Comparable接口

  1. 实参为null,compareTo方法应抛出NullPointerException
  2. 参数不合适,应抛出ClassCastException
  3. 比较对象引用域,通过递归比较
  4. 如果对象有多个域,则从最重要的域开始比较
  5. compareTo没有指定返回值的大小,而只是指定返回值的符号,可利用这点进行优化

例子:

public int compareTo(Object obj) {
        if(obj == null) {
            throw new NullPointerException(); // 满足第1点
        }
        if(!(obj instanceof MyComparable)) {
            throw new ClassCastException(); // 满足第2点
        }
        MyComparable o = (MyComparable) obj;
        int x = o.p.compareTo(p);// p 是一个对象 ,满足第3、4点
        if(x == 0) {
            return o.y - y;  // y 是个int型, 满足第5点,
           //对于第5点请注意,确保 o.y -y 不会溢出!!!
        }
        return x;
    }

二、Comparator(外部比较器)

可自己制定比较规则。在TreeSet中有应用,TresSet利用Comparator接口实现模板设计模式

文档中指出:

  1. 如果该对象大于指定对象,返回正整数
  2. 如果该对象等于指定对象,返回0
  3. 如果该对象小于指定对象,返回负整数
  4. 建议(x.compareTo(y)==0) == x.equals(y)
  5. 必须确保关系的传递性。即compare(x,y) > 0 且 compare(y,z) > 0,那么 compare(x,z) > 0

在本人看来,Comparable 与 Comparator 差别不大

疑问:那么Comparator具体的使用在什么地方呢?

JAVA API 的String 类 默认实现了Comparable接口,有默认的排序方式,但是如果我们想使用自己的排序方式呢?比如按照长度排序,那么就可以使用Comparator接口了。

class LengthComparator implements Comparator<String> {
    @Override
    public int compare(String o1, String o2) {
        return o1.length() - o2.length();
    }
}

心无私天地宽
513 声望22 粉丝