in java JDK8 Java8 函数式编程 functional programming 比较器 Comparator Comparable ~ read.

Java中的比较器

本文基于JDK 1.8.0_45


在Java中我们经常需要对数据进行排序,比如Collections.sort(list)或者Arrays.sort(array)。对于Java中的基本数据类型可以很简单的使用<,>,来进行比较,比如

int[] array = new int[]{2, 5, 3};  
Arrays.sort(array);  
for (int i : array) {  
    System.out.print(i + ", ");// 2, 3, 5,
}

但是对于对象而言使用只是进行比较对象的内存地址是否一样,如果重写了equals方法则可以使用自定义的规则进行判断两个对象是否一样。

Object o1 = new Object();  
Object o2 = new Object();  
System.out.println(o1 == o2);  
System.out.println(o1.equals(o2));  

在程序中对一系列的对象进行排序的时候,如何才能对对象使用自定义的规则进行比较呢?

在Java中有两种方式进行比较:

  1. 要比较的对象实现Comparable接口;
  2. 通过实现Comparator自定义比较器;

Comparable接口

该接口中只有一个方法compareTo,传入另一个同一类型的对象进行比较,返回负数、0、正数,分别代表小于、等于,大于。一般Comparable接口的实现称之为该类对象的自然排序。

public static void main(String[] args) {  
    Uncomparable u1 = new Uncomparable("u1");
    Uncomparable u2 = new Uncomparable("u2");
    Uncomparable u3 = new Uncomparable("u3");
    Uncomparable u4 = new Uncomparable("u4");

    List<Uncomparable> list = new ArrayList<>(Arrays.asList(u1, u2, u3, u4));
    Collections.sort(list);
    System.out.println(list);// [Uncomparable{name='u1'}, Uncomparable{name='u2'}, Uncomparable{name='u3'}, Uncomparable{name='u4'}]
}

static class Uncomparable implements Comparable<Uncomparable> {  
    private String name;

    public Uncomparable(String name) {
        this.name = name;
    }

    @Override
    public int compareTo(Uncomparable o) {
        return this.name.compareTo(o.name);
    }

    @Override
    public String toString() {
        return "Uncomparable{" +
                "name='" + name + '\'' +
                '}';
    }
}

请注意,强烈建议Comparable的实现和equals保持一致,即为o1.compareTo(o2) == 0和o1.equals(o2)有相同的结果。这是为了避免在使用SortedSet或SortedMap的时候出现异常的情况。

Comparator

Comparator中有三类方法:compare,静态的util方法,lambda相同的方法。

Comparator中的compare

与Comparable类似,传入两个同一类型的对象进行比较,返回负数、0、正数,分别代表第一个小于第二个、两个相等,第一个大于第二个。

Comparator一般用于以下场景:

  1. 类没有实现Comparable接口,并且该类无法进行修改,可以通过实现Comparator接口定义一个该类的比较器进行排序比较;
  2. 类实现了Comparable接口,但是对该类的对象进行排序时希望使用另一种方式进行排序比较;
public static void main(String[] args) {  
    Uncomparable u1 = new Uncomparable("u1");
    Uncomparable u2 = new Uncomparable("u2");
    Uncomparable u3 = new Uncomparable("u3");
    Uncomparable u4 = new Uncomparable("u4");

    System.out.println(u1.compareTo(u2));

    List<Uncomparable> list = new ArrayList<>(Arrays.asList(u1, u2, u3, u4));
    Collections.sort(list, new Comparator<Uncomparable>() {
        @Override
        public int compare(Uncomparable o1, Uncomparable o2) {
            return o2.compareTo(o1);
        };
    });
    System.out.println(list);// [Uncomparable{name='u4'}, Uncomparable{name='u3'}, Uncomparable{name='u2'}, Uncomparable{name='u1'}]
}

static class Uncomparable implements Comparable<Uncomparable> {  
    private String name;

    public Uncomparable(String name) {
        this.name = name;
    }

    @Override
    public int compareTo(Uncomparable o) {
        return this.name.compareTo(o.name);
    }

    @Override
    public String toString() {
        return "Uncomparable{" +
                "name='" + name + '\'' +
                '}';
    }
}

Comparator中的静态工具方法

public static void main(String[] args) {  
    Uncomparable u1 = new Uncomparable("u1");
    Uncomparable u2 = new Uncomparable("u2");
    Uncomparable u3 = new Uncomparable("u3");
    Uncomparable u4 = new Uncomparable("u4");

    List<Uncomparable> list = new ArrayList<>(Arrays.asList(u1, u2, u3, u4));
    Comparator<Uncomparable> comparator = new Comparator<Uncomparable>() {
        @Override
        public int compare(Uncomparable o1, Uncomparable o2) {
            return o1.compareTo(o2);
        };
    };

    Collections.sort(list, Comparator.naturalOrder());
    System.out.println(list);// [Uncomparable{name='u1'}, Uncomparable{name='u2'}, Uncomparable{name='u3'}, Uncomparable{name='u4'}]

    Collections.sort(list, Comparator.reverseOrder());
    System.out.println(list);// [Uncomparable{name='u4'}, Uncomparable{name='u3'}, Uncomparable{name='u2'}, Uncomparable{name='u1'}]

    list.add(null);

    Collections.sort(list, Comparator.nullsFirst(comparator));
    System.out.println(list);// [null, Uncomparable{name='u1'}, Uncomparable{name='u2'}, Uncomparable{name='u3'}, Uncomparable{name='u4'}]

    Collections.sort(list, Comparator.nullsLast(comparator));
    System.out.println(list);// [Uncomparable{name='u1'}, Uncomparable{name='u2'}, Uncomparable{name='u3'}, Uncomparable{name='u4'}, null]
}

static class Uncomparable implements Comparable<Uncomparable> {  
    private String name;

    public Uncomparable(String name) {
        this.name = name;
    }

    @Override
    public int compareTo(Uncomparable o) {
        return this.name.compareTo(o.name);
    }

    @Override
    public String toString() {
        return "Uncomparable{" +
                "name='" + name + '\'' +
                '}';
    }
}

如上所示,Comparator提供了四种静态方法,分为两类:

  1. naturalOrder和reverseOrder要求需要进行排序的对象必须实现了Comparable接口,一个提供默认的顺序,一个提供默认顺序的反序;
  2. 如果list中包含null的时候需要根据null排在前面还是后面的需求选择使用nullsFirst或nullsLast;

Comparator中的函数式编程相关的方法

另外Comparator还提供了一些函数式的静态方法和接口的默认方法:

public static void main(String[] args) {  
    Person u1 = new Person("u1", 15);
    Person u2 = new Person("u2", 12);
    Person u3 = new Person("u1", 29);
    Person u4 = new Person("U4", 3);

    List<Person> list = new ArrayList<>(Arrays.asList(u1, u2, u3, u4));

    Collections.sort(list, Comparator.comparing(Person::getName, String.CASE_INSENSITIVE_ORDER)
            .thenComparingInt(Person::getAge));
    System.out.println(list);// [Person{name='u1', age=15}, Person{name='u1', age=29}, Person{name='u2', age=12}, Person{name='U4', age=3}]
}

static class Person {  
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

如上所示,可以通过写调用链风格的代码来实现先通过一个比较器比较一个字段,再比较另一个字段的方法来定义比较器。

总结

Java中提供了丰富的API来进行比较:

  1. 可以通过实现Comparable接口让类拥有默认的比较器;
  2. 可以通过实现Comparator接口给类定义特殊的比较器;
  3. JDK8开始Comparator接口中提供了新的API来通过函数式编程的方式定义比较器;
  4. Collections和Arrays工具类中提供了对各种类型的数据进行排序的工具方法。
分享按钮