Java笔记——Comparable和Comparator

  参考了部分《Effective Java》第12条的内容和官方文档,刚好也被面试过,记一笔。

Comparable

  一个类一旦实现了Comparable接口,它就可以跟许多泛型算法以及依赖于该接口的集合实现进行写作。如果你编写的类需要进行排序,可以使该类实现Comparable接口。
  Comparable定义如下:

package java.lang;
import java.util.*;
public interface Comparable<T> {
    public int compareTo(T o);
}

  只包含了一个compareTo方法,将对象与指定的对象进行比较,当对象小于、等于或大于指定对象的时候,分别返回一个负整数、零或者正整数。符号sgn代表signum函数,它根据表达式的值为负值、零、正值分别返回-1,0,1。表达式需要满足的关系如下:

The implementor must ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for all x and y. (This implies that x.compareTo(y) must throw an exception iff y.compareTo(x) throws an exception.) 

The implementor must also ensure that the relation is transitive: (x.compareTo(y)>0 && y.compareTo(z)>0) implies x.compareTo(z)>0. 

Finally, the implementor must ensure that x.compareTo(y)==0 implies that sgn(x.compareTo(z)) == sgn(y.compareTo(z)), for all z. 

It is strongly recommended, but not strictly required that (x.compareTo(y)==0) == (x.equals(y)). Generally speaking, any class that implements the Comparable interface and violates this condition should clearly indicate this fact. The recommended language is "Note: this class has a natural ordering that is inconsistent with equals." 

  翻译过来即:

  • 实现者必须确保所有的x和y都满足sgn(x.compareTo(y)) ==-sgn(y.compareTo(x))。(这也意味着,当且仅当y.compareTo(x)抛出异常时,x.compareTo(y)才必须抛出异常)
      
  • 实现者还必须确保这个关系是可传递的:(x.compareTo(y)>0 && y.compareTo(z)>0)意味着x.compareTo(z)>0

  • 最后,实现者必须确保x.compareTo(y)==0暗示着所有的z都满足sgn(x.compareTo(z)) == sgn(y.compareTo(z))

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

      由compareTo是假方法是假的等同性测试,也他一定遵守相同于equals约定所施加的限制条件:自反性、对称性和传递性。
      下面举个例子:ComparableTest 实现了Comparable接口。它有三个属性,name,age,grade,现在需要对ComparableTest 对象实例进行排序,排序规则为先按name升序,再按age升序,最后按grade升序排,实现compareTo函数。

import java.util.Arrays;


public class ComparableTest implements Comparable<ComparableTest>{
    private String name ;
    private int age;
    private int grade;
    public ComparableTest(String name,int age,int grade) {
        this.name = name;
        this.age = age;
        this.grade = grade;
    }
    public static void main(String[] args) {
        ComparableTest s1 = new ComparableTest("zfq", 14, 70);
        ComparableTest s2 = new ComparableTest("abd", 12, 80);
        ComparableTest s3 = new ComparableTest("zfq", 13, 80);
        ComparableTest[] comp= {s1,s2,s3};
        Arrays.sort(comp);
        for(int i = 0;i < comp.length;i ++)
            System.out.println(comp[i].name + " " + comp[i].age + " " + comp[i].grade);
    }

    @Override
    public int compareTo(ComparableTest o) {
        if(name.compareTo(o.name) < 0)
            return -1;
        if(age < o.age)
            return -1;
        if(grade < o.grade)
            return -1;
        return 0;
    }
}

结果如下:

abd 12 80
zfq 13 80
zfq 14 70

Comparator

  如果我们需要对某个类进行排序,但是该类没有实现Comparable接口,那么我们可以自定义一个比较器,该比较器实现Comparator接口即可。
  Comparator定义如下:

package java.util;

public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

  该接口定义了两个方法compare(T o1, T o2)和equals(Objec)
  可以不重写equals方法,相当于Object.equals(Object)。comp1.equals(comp2) 意味着对于每一个对象引用o1,o2,sgn(comp1.compare(o1, o2))==sgn(comp2.compare(o1, o2))

  举个例子:
  ComparatorTest类没有实现Comparable接口,现要对ComparatorTest类对象进行排序,有两种排序
  (1)按name、age、grade升序排
  (2)按name、age、grade降序排
  定义了两个比较器AscComparator和DescComparator来实现这两种排序

import java.util.Arrays;
import java.util.Comparator;

public class ComparatorTest {
    private String name ;
    private int age;
    private int grade;
    public ComparatorTest(String name,int age,int grade) {
        this.name = name;
        this.age = age;
        this.grade = grade;
    }

    public static void main(String[] args) {
        ComparatorTest s1 = new ComparatorTest("zfq", 14, 70);
        ComparatorTest s2 = new ComparatorTest("abd", 12, 80);
        ComparatorTest s3 = new ComparatorTest("zfq", 13, 80);
        ComparatorTest[] comp= {s1,s2,s3};
        //按name,age,grade从小到大排
        Arrays.sort(comp, new AscComparator());
        System.out.println("升序后的结果:");
        for(int i = 0;i < comp.length;i ++)
            System.out.println(comp[i].name + " " + comp[i].age + " " + comp[i].grade);

        //按name,age,grade从大到小排
        Arrays.sort(comp, new DescComparator());
        System.out.println("降序后的结果:");
        for(int i = 0;i < comp.length;i ++)
            System.out.println(comp[i].name + " " + comp[i].age + " " + comp[i].grade);

    }
    //升序比较器
    private static class AscComparator implements Comparator<ComparatorTest> {

        @Override
        public int compare(ComparatorTest o1, ComparatorTest o2) {
            if(o1.name.compareTo(o2.name) < 0)
                return -1;
            if(o1.age < o2.age)
                return -1;
            if(o1.grade < o2.grade)
                return -1;
            return 0;
        }

    }
    //降序比较器
    private static class DescComparator implements Comparator<ComparatorTest> {

        @Override
        public int compare(ComparatorTest o1, ComparatorTest o2) {
            if(o1.name.compareTo(o2.name) > 0)
                return -1;
            if(o1.age > o2.age)
                return -1;
            if(o1.grade > o2.grade)
                return -1;
            return 0;
        }

    }

}

结果如下:

升序后的结果:
abd 12 80
zfq 13 80
zfq 14 70
降序后的结果:
zfq 14 70
zfq 13 80
abd 12 80

你可能感兴趣的:(Java)