Object Ordering对象排序

一个List可以如下排序:

Collections.sort(l);

如果List类型是String,将按字母顺序排序,如果是Date,将按时间排序。
因为String和Date都实现了 Comparable接口。Comparable的实现为类提供了自然排序,这允许自动排序该类的对象。下表总结了实现Comparable的一些重要的Java平台实现类。

Class Natural Ordering
Byte Signed numerical
Character Unsigned numerical
Long Signed numerical
Integer Signed numerical
Short Signed numerical
Double Signed numerical
Float Signed numerical
BigInteger Signed numerical
BigDecimal Signed numerical
Boolean Boolean.FALSE < Boolean.TRUE
File System-dependent lexicographic on path name
String Lexicographic
Date Chronological
CollationKey Locale-specific lexicographic

如果list没有实现Comparable接口,Collections.sort(list)会抛出 ClassCastException异常。
如果试图对一个不能使用comparator进行元素比较的列表进行排序,Collections.sort(list, comparator) 也会抛出ClassCastException。

自定义Comparable类型

Comparable接口:

public interface Comparable {
    public int compareTo(T o);
}

一个例子:

public class Name implements Comparable {
    private final String firstName, lastName;

    public Name(String firstName, String lastName) {
        if (firstName == null || lastName == null)
            throw new NullPointerException();
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String firstName() { return firstName; }
    public String lastName()  { return lastName;  }

    public boolean equals(Object o) {
        if (!(o instanceof Name))
            return false;
        Name n = (Name) o;
        return n.firstName.equals(firstName) && n.lastName.equals(lastName);
    }

    public int hashCode() {
        return 31*firstName.hashCode() + lastName.hashCode();
    }

    public String toString() {
    return firstName + " " + lastName;
    }

    public int compareTo(Name n) {
        int lastCmp = lastName.compareTo(n.lastName);
        return (lastCmp != 0 ? lastCmp : firstName.compareTo(n.firstName));
    }
}

为了保持前面示例的简短,该类有一定的限制:它不支持中间名,它要求有姓和名,并且它没有以任何方式国际化。
尽管如此,它阐明了以下要点:

  • Name对象是immutable不可变的。在其他条件相同的情况下,不可变类型是可行的,特别是在Set中用作元素或在Map中用作键的对象。如果在集合中修改它们的元素或键,这些集合将被破坏。
  • 构造函数检查它的参数是否为null,为null是抛出NullPointerException。
  • 重新定义了hashCode方法。这对于任何重定义equals方法的类都是必不可少的。(相等对象必须具有相等的哈希码。)
  • 如果指定的对象为null或类型不合适,则equals方法返回false。compare方法在这些情况下抛出运行时异常。这两种行为都是各自方法的一般契约所要求的。
  • 重定义toString方法。

compareTo的实现先比较lastName,如果lastName相等在比较firstName。

创建并排序一组Name:

public class NameSort {
    public static void main(String[] args) {
        Name nameArray[] = {
            new Name("John", "Smith"),
            new Name("Karl", "Ng"),
            new Name("Jeff", "Smith"),
            new Name("Tom", "Rich")
        };

        List names = Arrays.asList(nameArray);
        Collections.sort(names);
        System.out.println(names);
    }
}

Comparators

如果以特定规则排序而不是自然顺序或排序一些对象但又不想实现Comparable接口,只需要提供一个comparator:

public interface Comparator {
    int compare(T o1, T o2);
}

假设有一个Employee对象,假设其自然排序是之前定义在Name中的顺序:

public class Employee implements Comparable {
    public Name name()     { ... }
    public int number()    { ... }
    public Date hireDate() { ... }
       ...
}

现在要按级别排序:

import java.util.*;
public class EmpSort {
    static final Comparator SENIORITY_ORDER = 
                                        new Comparator() {
            public int compare(Employee e1, Employee e2) {
                return e2.hireDate().compareTo(e1.hireDate());
            }
    };

    // Employee database
    static final Collection employees = ... ;

    public static void main(String[] args) {
        List e = new ArrayList(employees);
        Collections.sort(e, SENIORITY_ORDER);
        System.out.println(e);
    }
}

Comparator无法用在有序集合上,比如TreeSet ,如果使用这个Comparator将多个同一天雇佣的employee插入到TreeSet,只要第一个雇员添加成功;第二个将被视为重复的元素并被忽略。
要解决这个问题,只需调整Comparator,使其生成一个与equals兼容的排序。换句话说,调整它,使在使用compare时被视为相等的元素是那些在使用equals进行比较时也被视为相等的元素。

static final Comparator SENIORITY_ORDER = 
                                        new Comparator() {
    public int compare(Employee e1, Employee e2) {
        int dateCmp = e2.hireDate().compareTo(e1.hireDate());
        if (dateCmp != 0)
            return dateCmp;

        return (e1.number() < e2.number() ? -1 :
               (e1.number() == e2.number() ? 0 : 1));
    }
};

你可能想把最后一句换成:

return e1.number() - e2.number();

不要这样做,除非你绝对确定没有人会有一个负的员工号码!这个技巧通常不起作用,因为有符号整数类型不足以表示两个任意有符号整数的差值。如果i是一个较大的正整数,而j是一个较大的负整数,i - j将溢出并返回一个负整数。由此产生的Comparator违反了我们一直在讨论的四个技术限制之一(传递性),并产生了可怕的、微妙的bug。这不是一个纯粹的理论问题。

你可能感兴趣的:(Object Ordering对象排序)