Comparator VS Comparable

Comparable

包名:java.lang

public interface Comparable<T> {
    /**
     * Compares this object with the specified object for order.  Returns a
     * negative integer, zero, or a positive integer as this object is less
     * than, equal to, or greater than the specified object.
     *
     * <p>The implementor must ensure <tt>sgn(x.compareTo(y)) ==
     * -sgn(y.compareTo(x))</tt> for all <tt>x</tt> and <tt>y</tt>.  (This
     * implies that <tt>x.compareTo(y)</tt> must throw an exception iff
     * <tt>y.compareTo(x)</tt> throws an exception.)
     *
     * <p>The implementor must also ensure that the relation is transitive:
     * <tt>(x.compareTo(y)&gt;0 &amp;&amp; y.compareTo(z)&gt;0)</tt> implies
     * <tt>x.compareTo(z)&gt;0</tt>.
     *
     * <p>Finally, the implementor must ensure that <tt>x.compareTo(y)==0</tt>
     * implies that <tt>sgn(x.compareTo(z)) == sgn(y.compareTo(z))</tt>, for
     * all <tt>z</tt>.
     *
     * <p>It is strongly recommended, but <i>not</i> strictly required that
     * <tt>(x.compareTo(y)==0) == (x.equals(y))</tt>.  Generally speaking, any
     * class that implements the <tt>Comparable</tt> 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."
     *
     * <p>In the foregoing description, the notation
     * <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
     * <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
     * <tt>0</tt>, or <tt>1</tt> according to whether the value of
     * <i>expression</i> is negative, zero or positive.
     *
     * @param   o the object to be compared.
     * @return  a negative integer, zero, or a positive integer as this object
     *          is less than, equal to, or greater than the specified object.
     *
     * @throws NullPointerException if the specified object is null
     * @throws ClassCastException if the specified object's type prevents it
     *         from being compared to this object.
     */
    public int compareTo(T o);
}
View Code

从源码compareTo(T o)中可以看出Comparable是一个内比较器,实现Comparable接口的类,都可以与自身比较,至于和另一个实现了Comparable接口的类如何比较,则依赖compareTo方法实现,compareTo方法也被称为自然比较法。如果开发者add进入一个Collection的对象想要Collections的sort方法帮你自动进行排序的话,那么这个对象必须实现Comparable接口。compareTo方法的返回值是int,有三种情况:

  • 比较者大于被比较者(也就是compareTo方法里面的对象),那么返回正整数
  • 比较者等于被比较者,那么返回0
  • 比较者小于被比较者,那么返回负整数

示例

 1 public class Student implements Comparable<Student >
 2 {
 3     private String name;
 4 
 5     public Student (String name)
 6     {
 7         this.name= name;
 8     }
 9 
10     public int compareTo(Student stu)
11     {
12         if (this.name.compareTo(stu.str) > 0)
13             return 1;
14         else if (this.name.compareTo(domain.name) == 0)
15             return 0;
16         else 
17             return -1;
18     }
19     
20     public String getName()
21     {
22         return name;
23     }
24 }
View Code

调用

public static void main(String[] args)
    {
        Student stu1 = new Student ("c");
        Student stu2 = new Student ("c");
        Student stu3 = new Student ("b");
        Student stu4 = new Student ("d");
        System.out.println(stu1.compareTo(stu2));
        System.out.println(stu1.compareTo(stu3));
        System.out.println(stu1.compareTo(stu4));
    }

注意: 前面说实现Comparable接口的类是可以支持和自己比较的,但是其实代码里面Comparable的泛型未必就一定要是Domain,将泛型指定为String或者指定为其他任何任何类型都可以----只要开发者指定了具体的比较算法就行。

 

Conparator

包名:java.util

@FunctionalInterface
public interface Comparator<T> {
    /**
     * Compares its two arguments for order.  Returns a negative integer,
     * zero, or a positive integer as the first argument is less than, equal
     * to, or greater than the second.<p>
     *
     * In the foregoing description, the notation
     * <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
     * <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
     * <tt>0</tt>, or <tt>1</tt> according to whether the value of
     * <i>expression</i> is negative, zero or positive.<p>
     *
     * The implementor must ensure that <tt>sgn(compare(x, y)) ==
     * -sgn(compare(y, x))</tt> for all <tt>x</tt> and <tt>y</tt>.  (This
     * implies that <tt>compare(x, y)</tt> must throw an exception if and only
     * if <tt>compare(y, x)</tt> throws an exception.)<p>
     *
     * The implementor must also ensure that the relation is transitive:
     * <tt>((compare(x, y)&gt;0) &amp;&amp; (compare(y, z)&gt;0))</tt> implies
     * <tt>compare(x, z)&gt;0</tt>.<p>
     *
     * Finally, the implementor must ensure that <tt>compare(x, y)==0</tt>
     * implies that <tt>sgn(compare(x, z))==sgn(compare(y, z))</tt> for all
     * <tt>z</tt>.<p>
     *
     * It is generally the case, but <i>not</i> strictly required that
     * <tt>(compare(x, y)==0) == (x.equals(y))</tt>.  Generally speaking,
     * any comparator that violates this condition should clearly indicate
     * this fact.  The recommended language is "Note: this comparator
     * imposes orderings that are inconsistent with equals."
     *
     * @param o1 the first object to be compared.
     * @param o2 the second object to be compared.
     * @return a negative integer, zero, or a positive integer as the
     *         first argument is less than, equal to, or greater than the
     *         second.
     * @throws NullPointerException if an argument is null and this
     *         comparator does not permit null arguments
     * @throws ClassCastException if the arguments' types prevent them from
     *         being compared by this comparator.
     */
    int compare(T o1, T o2);

    /**
     * Indicates whether some other object is &quot;equal to&quot; this
     * comparator.  This method must obey the general contract of
     * {@link Object#equals(Object)}.  Additionally, this method can return
     * <tt>true</tt> <i>only</i> if the specified object is also a comparator
     * and it imposes the same ordering as this comparator.  Thus,
     * <code>comp1.equals(comp2)</code> implies that <tt>sgn(comp1.compare(o1,
     * o2))==sgn(comp2.compare(o1, o2))</tt> for every object reference
     * <tt>o1</tt> and <tt>o2</tt>.<p>
     *
     * Note that it is <i>always</i> safe <i>not</i> to override
     * <tt>Object.equals(Object)</tt>.  However, overriding this method may,
     * in some cases, improve performance by allowing programs to determine
     * that two distinct comparators impose the same order.
     *
     * @param   obj   the reference object with which to compare.
     * @return  <code>true</code> only if the specified object is also
     *          a comparator and it imposes the same ordering as this
     *          comparator.
     * @see Object#equals(Object)
     * @see Object#hashCode()
     */
    boolean equals(Object obj);

  //其他再次不列出
}
View Code

从方法compare(T o1, T o2)可以看出Comparator是一个外比较器,有两种场景可能需要实现Comparator接口:

  • 一个对象不支持自己和自己比较(没有实现Comparable接口),但是又想对两个对象进行比较;
  • 一个对象实现了Comparable接口,但是开发者认为compareTo方法中的比较方式并不是自己想要的那种比较方式.

Comparator接口里面有一个compare方法,方法有两个参数T o1和T o2,是泛型的表示方式,分别表示待比较的两个对象,方法返回值和Comparable接口一样是int,有三种情况:

  • o1大于o2,返回正整数;
  • o1等于o2,返回0;
  • o1小于o3,返回负整数.

写个很简单的例子,上面代码的Student不变(假设这就是第2种场景,我对这个compareTo算法实现不满意,要自己写实现):

 1 public class StudentComparator implements Comparator<Student>
 2 {
 3     public int compare(Student stu1, Student stu2)
 4     {
 5         if (stu1.getName().compareTo(stu2.getName()) > 0)
 6             return 1;
 7         else if (stu2.getName().compareTo(stu2.getName()) == 0)
 8             return 0;
 9         else 
10             return -1;
11     }
12 }
View Code

调用示例:

public static void main(String[] args)
{
    Student s1 = new Student("c");
    Student s2 = new Student("c");
    Student s3 = new Student("b");
    Student s4 = new Student("d");
    StudentComparator sc = new StudentComparator();
    System.out.println(sc.compare(s1, s2));
    System.out.println(sc.compare(s1, s3));
    System.out.println(sc.compare(s1, s4));
}

当然因为泛型已指定,所以实现Comparator接口的实现类只能是两个相同的对象(不能一个Domain、一个String)进行比较了,因此实现Comparator接口的实现类一般都会以"待比较的实体类+Comparator"来命名

 

 

从Conparable和Comparator的示例可以看出:

  • 如果实现类没有实现Comparable接口,又想对两个类进行比较(或者实现类实现了Comparable接口,但是对compareTo方法内的比较算法不满意),那么可以实现Comparator接口,自定义一个比较器,写比较算法
  • 实现Comparable接口的方式比实现Comparator接口的耦合性要强一些,如果要修改比较算法,要修改Comparable接口的实现类,而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修改。从这个角度说,其实有些不太好,尤其在我们将实现类的.class文件打成一个.jar文件提供给开发者使用的时候。实际上实现Comparator接口的方式后面会写到就是一种典型的策略模式
posted @ 2020-12-18 20:15  工作中的点点滴滴  阅读(118)  评论(0编辑  收藏  举报