[java 排序] Comparable 和 Comparator

时间:2022-09-02 02:50:59

Comparable简介

接口定义:

package java.lang;
import java.util.*;

public interface Comparable<T> 
{
    public int compareTo(T o);
}

文档:

此接口强行对实现它的每个类的对象进行整体排序。这种排序称为类的自然(native)排序。类的compareTo方法被称为它的自然(native)比较方法
列表(或数组)中的元素如果实现此接口,那么该列表(或数组)可以通过Collection.sort()Arrays.sort()进行自动的排序。实现此接口的对象可以用作SortedMap的key或者SortedSet的元素,而不用额外指定比较器(comparator)

对于类C的实例e1,e2来说仅当e1.compareTo(e2) == 0e1.equals(e2)具有相同的boolean值时,类C的自然排序才叫做与equals一致。注意null 不是任何类的实例,e.compareTo(null)应当抛出NullPointerException即使e.equals(null)返回false

建议(虽然不是必需的)最好使自然排序与 equals 一致。这是因为在使用自然排序与 equals 不一致的元素(或键)时,没有显式比较器的SortedSet(和SortedMap)行为表现“怪异”。尤其是,这样的有序集合(或有序映射表)违背了根据 equals 方法定义的集合(或映射表)的常规协定。

例如,如果将两个键 a 和 b 添加到没有使用显式比较器的有序集合中,使 (!a.equals(b) && a.compareTo(b) == 0),那么第二个 add 操作将返回 false(有序集合的大小没有增加),因为从有序集合的角度来看,a 和 b 是相等的。

实际上,所有实现 Comparable 的 Java 核心类都具有与 equals 一致的自然排序。java.math.BigDecimal 是个例外,它的自然排序将值相等但精确度不同的 BigDecimal 对象(比如 4.0 和 4.00)视为相等

从数学上讲,定义给定类 C 上自然排序的关系式 如下:

  {(x, y)|x.compareTo(y) <= 0}。

整体排序的 商 是:
{(x, y)|x.compareTo(y) == 0}。
它直接遵循 compareTo 的协定,商是 C 的 等价关系,自然排序是 C 的 整体排序。当说到类的自然排序 与 equals 一致 时,是指自然排序的商是由类的 equals(Object) 方法定义的等价关系。
{(x, y)|x.equals(y)}。

public int compareTo(T o)方法介绍

文档:

比较此对象与指定对象的顺序。如果该对象小于、等于、大于指定的对象,则分别返回负整数、零、或正整数。
实现类对于所有的实例x, y必须确保sgn(x.compareTo(y)) == -sng(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 不一致的自然排序。”

在前面的描述中,符号 sgn(expression) 指定 signum 数学函数,该函数根据 expression 的值是负数、零还是正数,分别返回 -1、0 或 1 中的一个值。

Compatator简介

定义

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

此类的文档与Compable的文档相似,
compare方法的介绍也与Compable.compareTo方法的介绍相似
在此不过多介绍。

equals方法

文档:

指示某个其他对象是否“等于”此 Comparator。此方法必须遵守 Object.equals(Object) 的常规协定。此外, 仅当 指定的对象也是一个 Comparator,并且强行实施与此 Comparator 相同的排序时,此方法才返回 true。因此,comp1.equals(comp2) 意味着对于每个对象引用 o1o2 而言,都存在 sgn(comp1.compare(o1, o2))==sgn(comp2.compare(o1, o2))

注意,不 重写 Object.equals(Object) 方法总是 安全的。然而,在某些情况下,重写此方法可以允许程序确定两个不同的 Comparator 是否强行实施了相同的排序,从而提高性能。

Comparable和Comparator区别比较

Comparable是排序接口,若一个类实现了Comparable接口,就意味着“该类支持排序”。而Comparator是比较器,我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。

Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。

两种方法各有优劣:
用Comparable 简单, 只要实现Comparable 接口的对象直接就成为一个可以比较的对象,但是需要修改源代码。
用Comparator 的好处是不需要修改源代码, 而是另外实现一个比较器, 当某个自定义的对象需要作比较的时候,把比较器和对象一起传递过去就可以比大小了, 并且在Comparator 里面用户可以自己实现复杂的可以通用的逻辑,使其可以匹配一些比较简单的对象,那样就可以节省很多重复劳动了。

参考