该文基于JDK1.8。
一、Comparable<T>
Comparable<T>源码如下:
package java.lang;
import java.util.*;
/**
* This interface imposes a total ordering on the objects of each class that
* implements it. This ordering is referred to as the class's <i>natural
* ordering</i>, and the class's <tt>compareTo</tt> method is referred to as
* its <i>natural comparison method</i>.<p>
*
* Lists (and arrays) of objects that implement this interface can be sorted
* automatically by {@link Collections#sort(List) Collections.sort} (and
* {@link Arrays#sort(Object[]) Arrays.sort}). Objects that implement this
* interface can be used as keys in a {@linkplain SortedMap sorted map} or as
* elements in a {@linkplain SortedSet sorted set}, without the need to
* specify a {@linkplain Comparator comparator}.<p>
*
* The natural ordering for a class <tt>C</tt> is said to be <i>consistent
* with equals</i> if and only if <tt>e1.compareTo(e2) == 0</tt> has
* the same boolean value as <tt>e1.equals(e2)</tt> for every
* <tt>e1</tt> and <tt>e2</tt> of class <tt>C</tt>. Note that <tt>null</tt>
* is not an instance of any class, and <tt>e.compareTo(null)</tt> should
* throw a <tt>NullPointerException</tt> even though <tt>e.equals(null)</tt>
* returns <tt>false</tt>.<p>
*
* It is strongly recommended (though not required) that natural orderings be
* consistent with equals. This is so because sorted sets (and sorted maps)
* without explicit comparators behave "strangely" when they are used with
* elements (or keys) whose natural ordering is inconsistent with equals. In
* particular, such a sorted set (or sorted map) violates the general contract
* for set (or map), which is defined in terms of the <tt>equals</tt>
* method.<p>
*
* For example, if one adds two keys <tt>a</tt> and <tt>b</tt> such that
* {@code (!a.equals(b) && a.compareTo(b) == 0)} to a sorted
* set that does not use an explicit comparator, the second <tt>add</tt>
* operation returns false (and the size of the sorted set does not increase)
* because <tt>a</tt> and <tt>b</tt> are equivalent from the sorted set's
* perspective.<p>
*
* Virtually all Java core classes that implement <tt>Comparable</tt> have natural
* orderings that are consistent with equals. One exception is
* <tt>java.math.BigDecimal</tt>, whose natural ordering equates
* <tt>BigDecimal</tt> objects with equal values and different precisions
* (such as 4.0 and 4.00).<p>
*
* For the mathematically inclined, the <i>relation</i> that defines
* the natural ordering on a given class C is:<pre>
* {(x, y) such that x.compareTo(y) <= 0}.
* </pre> The <i>quotient</i> for this total order is: <pre>
* {(x, y) such that x.compareTo(y) == 0}.
* </pre>
*
* It follows immediately from the contract for <tt>compareTo</tt> that the
* quotient is an <i>equivalence relation</i> on <tt>C</tt>, and that the
* natural ordering is a <i>total order</i> on <tt>C</tt>. When we say that a
* class's natural ordering is <i>consistent with equals</i>, we mean that the
* quotient for the natural ordering is the equivalence relation defined by
* the class's {@link Object#equals(Object) equals(Object)} method:<pre>
* {(x, y) such that x.equals(y)}. </pre><p>
*
* This interface is a member of the
* <a href="{@docRoot}/../technotes/guides/collections/index.html">
* Java Collections Framework</a>.
*
* @param <T> the type of objects that this object may be compared to
*
* @author Josh Bloch
* @see java.util.Comparator
* @since 1.2
*/
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);
}
去掉注释部分,简化后如下:
public interface Comparable<T> {
public int compareTo(T o);
}
1、public interface Comparable<T>
Comparable是一个泛型接口,其实从该接口的命名上我们就可以看出,一般在Java中,会使用形容词对接口进行命名,比如Serializable,但也有很多例外。该接口会对它的每一个实现类的对象施加一个全序(total ordering),后面我们简称为序,这个全序被称为实现类的自然顺序(natural ordering),实现类中具体实现的compareTo方法被称为该类的自然比较方法(natural comparison method)。也就是该接口可以赋予它的实现类一个自然顺序,就像整数一样,会有个自然顺序,这里整数就可以看做是实现类,具体的整数可以看成是该类的某个对象;0,1,2,……,100,……,这个顺序就可以看成是整数的自然顺序。
关于该接口有如下几点说明:
(1)某个类C的自然顺序被称为与它的equals方法是一致的,是指对类C的所有实例(instance)x和y,x.compareTo(y)==0和x.equals(y)有相同的boolean值,false或者true。注意null不是任何类的实例。事实上x.compareTo(null)会抛NullPointerException空指针异常,当x不为null时,x.equals(null)会返回false,x为null时也会抛出NullPointerException异常。强烈建议类的compareTo方法实现与equals方法实现保持一致。例如:
package main.compare;
/**
* Created by leboop on 2018/11/18.
*/
public class Person implements Comparable<Person> {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person person) {
return this.age - person.age;
}
}
Person类实现了Comparable接口,按照属性age定义了自然顺序。但并未对equals方法重写,直接继承了父类Object的equals方法。测试类如下:
package main.compare;
import java.util.*;
/**
* Created by leboop on 2018/11/18.
*/
public class CompareTest {
public static void main(String[] args) {
Person p1 = new Person("zhangsan", 10);
Person p2 = new Person("lisi", 10);
Person p3 = new Person("wangwu", 10);
SortedSet<Person> sortedSet=new TreeSet();
boolean flag1=sortedSet.add(p1);
boolean flag2=sortedSet.add(p2);
boolean flag3=sortedSet.add(p3);
System.out.println("flag1:"+flag1);
System.out.println("flag2:"+flag2);
System.out.println("flag3:"+flag3);
System.out.println(sortedSet);
}
}
该测试类定义了三个Person对象p1、p2和p3,按照equals方法判断,他们是三个对象,按照序来看,三个对象的序是一样的,当我们将他们加入到一个有排序的集合中,p2和p3会加入失败,测试类运行结果如下:
flag1:true
flag2:false
flag3:false
集合大小:1
(2)事实上,实现了Comparable的所有Java核心类的自然顺序都与equals方法是一致的,一个特例是java.math.BigDecimal。
package main.compare;
import java.math.BigDecimal;
import java.util.*;
/**
* Created by leboop on 2018/11/18.
*/
public class CompareTest {
public static void main(String[] args) {
BigDecimal d1=new BigDecimal("1.0");
BigDecimal d2=new BigDecimal("1.00");
System.out.println("d1的精度:"+d1.scale());
System.out.println("d2的精度:"+d2.scale());
System.out.println("d1.equals(d2):"+d1.equals(d2));
System.out.println("d1.compareTo(d2):"+d1.compareTo(d2));
}
}
程序运行结果:
d1的精度:1
d2的精度:2
d1.equals(d2):false
d1.compareTo(d2):0
Process finished with exit code 0
对于BigDecimal类,equals会比较值和精度,compareTo比较值大小。
2、public int compareTo(T o)
该方法用于比较当前对象(this object)和给定对象(specified object)的序,它的返回值负整数,零,正整数分别对应当前对象小于,等于和大于给定对象。compareTo在子类的具体实现有以下几点必须保证的:
(1)对所有的对象x和y满足sgn(x.compareTo(y))等于-sgn(y.compareTo(x)),要么x.compareTo(y)抛出异常当且仅当x.compareTo(y)抛异常。这里sgn(x)是一个数学符号函数,当x>0时,sgn(x)=1;当x=0时,sgn(x)=0;当x<0时,sgn(x)=-1;
(2)如果x.compareTo(y)大于0,并且y.compareTo(z)大于0,那么x.compareTo(z)一定大于0;
(3)如果x.compareTo(y)等于0,那么对所有的z满足sgn(x.compareTo(z))和sgn(x.compareTo(z))相等;
compareTo在子类的具体实现中有一点是强烈推荐的(但不是必须),就是(x.compareTo(y)==0) == (x.equals(y))。如果实现Comparable接口的类违反了这个条件,应当清晰的表明这个事实。推荐使用语言“Note: this class has a natural ordering that is inconsistent with equals.”,大致意思就是说该类的自然顺序与类的equals方法不一致。
当给定对象是null时,该方法会抛出NullPointerException异常,如果给定的对象类型不能和当前对象进行比较会排出ClassCastException。
Comparable总结:
(1)某个类实现了Comparable接口就赋予了一个自然顺序
(2)compareTo方法实现时需要与equals方法保持一致
二、Comparator<T>
源码:
package java.util;
import java.io.Serializable;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.function.ToDoubleFunction;
import java.util.Comparators;
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
default Comparator<T> thenComparing(Comparator<? super T> other) {
Objects.requireNonNull(other);
return (Comparator<T> & Serializable) (c1, c2) -> {
int res = compare(c1, c2);
return (res != 0) ? res : other.compare(c1, c2);
};
}
default <U> Comparator<T> thenComparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
return thenComparing(comparing(keyExtractor, keyComparator));
}
default <U extends Comparable<? super U>> Comparator<T> thenComparing(
Function<? super T, ? extends U> keyExtractor)
{
return thenComparing(comparing(keyExtractor));
}
default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) {
return thenComparing(comparingInt(keyExtractor));
}
default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) {
return thenComparing(comparingLong(keyExtractor));
}
default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) {
return thenComparing(comparingDouble(keyExtractor));
}
public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
return Collections.reverseOrder();
}
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
}
public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
return new Comparators.NullComparator<>(true, comparator);
}
public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) {
return new Comparators.NullComparator<>(false, comparator);
}
public static <T, U> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
Objects.requireNonNull(keyExtractor);
Objects.requireNonNull(keyComparator);
return (Comparator<T> & Serializable)
(c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
keyExtractor.apply(c2));
}
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
}
public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
}
public static<T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2));
}
}
1、Comparator<T>是一个比较器接口,被比较的类不需要直接实现它,例如Person类如下:
package main.compare;
/**
* Created by leboop on 2018/11/18.
*/
public class Person {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
现在想对Person的对象按照年龄进行降序排序,我们只需要定义一个Person类的降序比较器,如下:
package main.compare;
import java.util.Comparator;
/**
* Created by leboop on 2018/11/18.
*/
public class DesComparator implements Comparator<Person> {
@Override
public int compare(Person o1, Person o2) {
return o2.getAge()-o1.getAge();
}
}
测试类如下:
package main.compare;
import java.math.BigDecimal;
import java.util.*;
/**
* Created by leboop on 2018/11/18.
*/
public class CompareTest {
public static void main(String[] args) {
Person p1=new Person("zhangsan",10);
Person p2=new Person("lisi",29);
Person p3=new Person("wangwu",25);
Person p4=new Person("mazi",34);
List<Person> personList=new ArrayList<>();
personList.add(p1);
personList.add(p2);
personList.add(p3);
personList.add(p4);
System.out.println("排序前:"+personList);
Collections.sort(personList,new DesComparator());
System.out.println("排序后:"+personList);
}
}
运行结果如下:
排序前:[Person{name='zhangsan', age=10}, Person{name='lisi', age=29}, Person{name='wangwu', age=25}, Person{name='mazi', age=34}]
排序后:[Person{name='mazi', age=34}, Person{name='lisi', age=29}, Person{name='wangwu', age=25}, Person{name='zhangsan', age=10}]
Process finished with exit code 0
2、对Comparator的compare方法实现有和Comparable接口对compareTo方法实现同样的要求。
三、Comparable和Comparator区别
1、对于Comparable接口来书,被比较对象所属的类需要直接实现Comparable接口,实现该接口的类被赋予一个自然顺序,实现该接口的类的自然顺序只有一个,而Comparator是一个比较器接口,被比较对象所属的类不需要直接实现该接口,可以单独写一个比较器类实现该接口,作为比较对象的一个比较器,对于一个类来说,可以实现多个比较器。
2、Comparator可以选择对null进行比较,而Comparable不可以。主要是因为Comparator的比较对象是compare方法的参数,而Comparable的比较方法compareTo方法需要对象来调用,而对象为null时(null.compareTo(obj)),会出现异常。