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)>0 && y.compareTo(z)>0)</tt> implies * <tt>x.compareTo(z)>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); }
从源码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 }
调用
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)>0) && (compare(y, z)>0))</tt> implies * <tt>compare(x, z)>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 "equal to" 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); //其他再次不列出 }
从方法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 }
调用示例:
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接口的方式后面会写到就是一种典型的策略模式。