Comparable接口和Comparator接口

Java中提供了两种对集合或数组中元素进行排序的方法,一种是实现Comparable接口,另一种是实现Comparator接口。

虽然这两种接口看起来差不多,但是在使用方法上有比较大的差别,下面我将开始介绍这两种接口以及它们的一些应用场景。

接口名称 主要函数
Comparable java.lang int compareTo(T o)(比较此对象与指定对象的顺序)
Comparator java.util int compare(T o1,T o2)(比较用来排序的两个参数) ,boolean equals(Object obj)(指示某个其他对象是否“等于”此Comparator)

一、Comparable接口

public interface Comparable<T>

下面这段介绍来自官方文档:

此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。
实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序。实现此接口的可以用作有序映射(实现了SortedMap接口的对象)有序集合(实现了SortedSet接口的对象)中的元素,无需指定比较器。
建议(虽然不是必需的)最好使自然排序与equals一致。
所谓自然排序与equals一致指的是 类A 对于每一个 o1 和 o2 来说,当且仅当 ( o1.compareTo( o2 ) )与 o1.equals( o2 )具有相同的 布尔值 时,类A的自然排序才叫做与equals一致。

下面将文档中出现的几个点抽出来单独理解:

  • 自然排序:Comparable接口强行对实现它的类的对象进行整体排序,这样的排序称为该类的自然排序;
  • 自然排序与equals一致:类A 对于每一个 o1 和 o2 来说,当且仅当 ( o1.compareTo( o2 ) )与 o1.equals( o2 )具有相同的 布尔值 时,类A的自然排序才叫做与equals一致。

方法详细信息:

int compareTo(T o) 比较此对象与指定对象的顺序

比较此对象与指定对象的顺序,如果该对象小于、等于、或大于指定对象,则分别返回负整数、零或正整数。
实现类必须确保对于所有的 x 和 y 都存在 sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) 的关系。(这意味着如果 y.compareTo(x) 抛出一个异常,则 x.compareTo(y) 也要抛出一个异常。)

  • 参数: o - 要比较的对象
  • 返回: 负整数、零或正整数,根据此对象是小于、等于还是大于指定对象
  • 抛出: CalssCastException - 如果指定对象的类型不允许它与此对象进行比较

通过一个小例子来介绍如何使用Comparable接口:
下面是一个普通的Person类,name和age两个属性以及对应的get和set方法。

没有实现Comparable接口的Person类

下面是一个PersonTest测试类以及打印的结果:
我们往一个list当中添加了五个Person对象,然后将此list中的内容打印输出,发现输出的顺序和添加的顺序是一致的。
测试类

打印结果

如果我们此时需要按照Person对象的年龄从小到大输出,该如何实现呢?
这个时候Comparable接口就派上用场了

我们把Person改造一下,让它实现Comparable<T>接口并重写其compareTo方法:

实现了Comparable接口的Person类

然后在测试类中调用 Collections.sort() 方法对list进行排序之后输出结果:

使用了Collections.sortf方法之后的结果

我们可以注意,其实我们在这里显式地指定了比较器:Collection.sort()。
但是对于那些实现了 SortedMap接口 的对象或者是实现了 SortedSet接口 的对象来说,无需指定比较器即可完成我们所需的功能。

使用实现了SortedSet接口的TreeSet无需指定比较器

二、Comparator接口

public interface Comparator<T>

下面这段介绍来自官方文档:

强行对某个对象collection进行整体排序的比较函数。可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set 或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。
当且仅当对于一组元素 S 中的每个 e1 和 e2 而言,c.compare(e1, e2)==0 与 e1.equals(e2) 具有相等的布尔值时,Comparator c 强行对 S 进行的排序才叫做与 equals 一致 的排序。
当使用具有与 equals 不一致的强行排序能力的 Comparator 对有序 set(或有序映射)进行排序时,应该小心谨慎。假定一个带显式 Comparator c 的有序 set(或有序映射)与从 set S 中抽取出来的元素(或键)一起使用。如果 c 强行对 S 进行的排序是与 equals 不一致的,那么有序 set(或有序映射)将是行为“怪异的”。尤其是有序 set(或有序映射)将违背根据 equals 所定义的 set(或映射)的常规协定。
例如,假定使用 Comparator c 将满足 (a.equals(b) && c.compare(a, b) != 0) 的两个元素 a 和 b 添加到一个空 TreeSet 中,则第二个 add 操作将返回 true(树 set 的大小将会增加),因为从树 set 的角度来看,a 和 b 是不相等的,即使这与 Set.add 方法的规范相反。
注:通常来说,让Comparator也实现 java.io.Serializable 是一个好主意,因为它们在可序列化的数据结构(像 TreeSet 、TreeMap)中可用作排序方法。为了成功地序列化数据结构,Comparator(如果已提供)必须实现Serializable。

方法详细信息:

int compare(T o1, T o2) 比较用来排序的两个参数

比较用来排序的两个参数。根据第一个参数小于、等于或大于第二个参数分别返回负整数、零或正整数。

boolean equals(Object obj) 指示某个其他对象是否“等于”此Comparator

指示某个其他对象是否“等于”此 Comparator。此方法必须遵守 Object.equals(Object)的常规协定。此外,仅当指定的对象也是一个Comparator,并且强行实施与此 Comparator 相同的排序时,此方法才返回true。因此,comp1.equals(comp2)意味着对于每个对象引用o1和o2而言,都存在sgn(comp1.compare(o1, o2))==sgn(comp2.compare(o1, o2))。注意,不重写Object.equals(Object)方法总是安全的。然而,在某些情况下,重写此方法可以允许程序确定两个不同的 Comparator 是否强行实施了相同的排序,从而提高性能。

下面继续使用Person类来说说该如何通过使用Comparator<T>接口来帮助我们实现排序:
Person类使用的还是上面那个没有实现任何接口的Person类。
那么我们该如何对Person类的对象进行排序呢?
****其实只要合理使用Comparator接口就行了,如下图:**

通过实现Comparator接口来对集合进行排序
现在我们可以来回答下面这个问题了:

TreeSet和TreeMap在排序时如何比较元素?Collections工具类中的sort( )方法如何比较元素?

TreeSet要求存放的对象所属类必需实现Comparable接口,该接口提供了比较元素的 compareTo( ) 方法,当插入元素的时候会回调该方法比较元素的大小。
TreeMap要求存放的键值对映射的键必需实现Comparable接口从而根据键对元素进行排序。
另外Collections工具类中的 sort( ) 方法有两种重载形式:

  • 第一种要求传入的待排序容器中存放的对象比较实现 Comaprable 接口以实现元素的比较;
  • 第二种不强制的要求容器中的元素必须可以进行比较,但是要求传入的第二个参数,参数是 Comparator 接口的子类型(需要重写 compare 方法实现元素的比较),相当于临时定义一个排序规则,通过接口注入比较元素的大小,也就是对回调模式的应用。

附加问题:如果对ArrayList进行排序?
其实我在上面的程序当中已经演示得很清楚了。ArrayList不像TreeMap和TreeSet,可以在元素插入的过程中进行排序。我们可以在元素全部插入完毕时候通过调用Collections工具类的 sort( ) 方法对里面的元素进行排序。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,169评论 11 349
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,560评论 18 399
  • 最近Algorithms 4 课上提到了排序。趁着这个机会,梳理一下。 1. 介绍 Comparable<T>接口...
    深海__阅读 669评论 0 1
  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,253评论 0 16
  • 项目中经常会遇到列表搜索查询,大部分的查询是可以通过sql语句来实现的,有些特殊的搜索排序sql则实现不了,例如中...
    信徒_allen阅读 2,571评论 0 1