Java Comparable 和 Comparator 的詳解及區(qū)別
Java Comparable 和 Comparator 的詳解及區(qū)別
Java 中為我們提供了兩種比較機(jī)制:Comparable 和 Comparator,他們之間有什么區(qū)別呢?今天來(lái)了解一下。
Comparable 自然排序
Comparable 在 java.lang 包下,是一個(gè)接口,內(nèi)部只有一個(gè)方法 compareTo():
public interface Comparable<T> { public int compareTo(T o); }
Comparable 可以讓實(shí)現(xiàn)它的類的對(duì)象進(jìn)行比較,具體的比較規(guī)則是按照 compareTo 方法中的規(guī)則進(jìn)行。這種順序稱為 自然順序。
compareTo 方法的返回值有三種情況:
- e1.compareTo(e2) > 0 即 e1 > e2
- e1.compareTo(e2) = 0 即 e1 = e2
- e1.compareTo(e2) < 0 即 e1 < e2
注意:
1.由于 null 不是一個(gè)類,也不是一個(gè)對(duì)象,因此在重寫(xiě) compareTo 方法時(shí)應(yīng)該注意 e.compareTo(null) 的情況,即使 e.equals(null) 返回 false,compareTo 方法也應(yīng)該主動(dòng)拋出一個(gè)空指針異常 NullPointerException。
2.Comparable 實(shí)現(xiàn)類重寫(xiě) compareTo 方法時(shí)一般要求 e1.compareTo(e2) == 0 的結(jié)果要和 e1.equals(e2) 一致。這樣將來(lái)使用 SortedSet 等根據(jù)類的自然排序進(jìn)行排序的集合容器時(shí)可以保證保存的數(shù)據(jù)的順序和想象中一致。
有人可能好奇上面的第二點(diǎn)如果違反了會(huì)怎樣呢?
舉個(gè)例子,如果你往一個(gè) SortedSet 中先后添加兩個(gè)對(duì)象 a 和 b,a b 滿足 (!a.equals(b) && a.compareTo(b) == 0),同時(shí)也沒(méi)有另外指定個(gè) Comparator,那當(dāng)你添加完 a 再添加 b 時(shí)會(huì)添加失敗返回 false, SortedSet 的 size 也不會(huì)增加,因?yàn)樵?SortedSet 看來(lái)它們是相同的,而 SortedSet 中是不允許重復(fù)的。
實(shí)際上所有實(shí)現(xiàn)了 Comparable 接口的 Java 核心類的結(jié)果都和 equlas 方法保持一致。
實(shí)現(xiàn)了 Comparable 接口的 List 或則數(shù)組可以使用 Collections.sort() 或者 Arrays.sort() 方法進(jìn)行排序。
實(shí)現(xiàn)了 Comparable 接口的對(duì)象才能夠直接被用作 SortedMap (SortedSet) 的 key,要不然得在外邊指定 Comparator 排序規(guī)則。
因此自己定義的類如果想要使用有序的集合類,需要實(shí)現(xiàn) Comparable 接口,比如:
** * description: 測(cè)試用的實(shí)體類 書(shū), 實(shí)現(xiàn)了 Comparable 接口,自然排序 * <br/> * author: shixinzhang * <br/> * data: 10/5/2016 */ public class BookBean implements Serializable, Comparable { private String name; private int count; public BookBean(String name, int count) { this.name = name; this.count = count; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } /** * 重寫(xiě) equals * @param o * @return */ @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof BookBean)) return false; BookBean bean = (BookBean) o; if (getCount() != bean.getCount()) return false; return getName().equals(bean.getName()); } /** * 重寫(xiě) hashCode 的計(jì)算方法 * 根據(jù)所有屬性進(jìn)行 迭代計(jì)算,避免重復(fù) * 計(jì)算 hashCode 時(shí) 計(jì)算因子 31 見(jiàn)得很多,是一個(gè)質(zhì)數(shù),不能再被除 * @return */ @Override public int hashCode() { //調(diào)用 String 的 hashCode(), 唯一表示一個(gè)字符串內(nèi)容 int result = getName().hashCode(); //乘以 31, 再加上 count result = 31 * result + getCount(); return result; } @Override public String toString() { return "BookBean{" + "name='" + name + '\'' + ", count=" + count + '}'; } /** * 當(dāng)向 TreeSet 中添加 BookBean 時(shí),會(huì)調(diào)用這個(gè)方法進(jìn)行排序 * @param another * @return */ @Override public int compareTo(Object another) { if (another instanceof BookBean){ BookBean anotherBook = (BookBean) another; int result; //比如這里按照書(shū)價(jià)排序 result = getCount() - anotherBook.getCount(); //或者按照 String 的比較順序 //result = getName().compareTo(anotherBook.getName()); if (result == 0){ //當(dāng)書(shū)價(jià)一致時(shí),再對(duì)比書(shū)名。 保證所有屬性比較一遍 result = getName().compareTo(anotherBook.getName()); } return result; } // 一樣就返回 0 return 0; }
上述代碼還重寫(xiě)了 equlas(), hashCode() 方法,自定義的類想要進(jìn)行比較時(shí)都要重寫(xiě)這些方法。
后面重寫(xiě) compareTo 時(shí),要判斷某個(gè)相同時(shí)對(duì)比下一個(gè)屬性,把所有屬性都比較一次。
Comparable 接口屬于 Java 集合框架的一部分。
Comparator 定制排序
Comparator 在 java.util 包下,也是一個(gè)接口,JDK 1.8 以前只有兩個(gè)方法:
public interface Comparator<T> { public int compare(T lhs, T rhs); public boolean equals(Object object); }
JDK 1.8 以后又新增了很多方法:
基本上都是跟 Function 相關(guān)的,這里暫不介紹 1.8 新增的。
從上面內(nèi)容可知使用自然排序需要類實(shí)現(xiàn) Comparable,并且在內(nèi)部重寫(xiě) comparaTo 方法。
而 Comparator 則是在外部制定排序規(guī)則,然后作為排序策略參數(shù)傳遞給某些類,比如 Collections.sort(), Arrays.sort(), 或者一些內(nèi)部有序的集合(比如 SortedSet,SortedMap 等)。
使用方式主要分三步:
1.創(chuàng)建一個(gè) Comparator 接口的實(shí)現(xiàn)類,并賦值給一個(gè)對(duì)象
在 compare 方法中針對(duì)自定義類寫(xiě)排序規(guī)則
2.將 Comparator 對(duì)象作為參數(shù)傳遞給 排序類的某個(gè)方法
3.向排序類中添加 compare 方法中使用的自定義類
舉個(gè)例子:
// 1.創(chuàng)建一個(gè)實(shí)現(xiàn) Comparator 接口的對(duì)象 Comparator comparator = new Comparator() { @Override public int compare(Object object1, Object object2) { if (object1 instanceof NewBookBean && object2 instanceof NewBookBean){ NewBookBean newBookBean = (NewBookBean) object1; NewBookBean newBookBean1 = (NewBookBean) object2; //具體比較方法參照 自然排序的 compareTo 方法,這里只舉個(gè)栗子 return newBookBean.getCount() - newBookBean1.getCount(); } return 0; } }; //2.將此對(duì)象作為形參傳遞給 TreeSet 的構(gòu)造器中 TreeSet treeSet = new TreeSet(comparator); //3.向 TreeSet 中添加 步驟 1 中 compare 方法中設(shè)計(jì)的類的對(duì)象 treeSet.add(new NewBookBean("A",34)); treeSet.add(new NewBookBean("S",1)); treeSet.add( new NewBookBean("V",46)); treeSet.add( new NewBookBean("Q",26));
其實(shí)可以看到,Comparator 的使用是一種策略模式,不熟悉策略模式的同學(xué)可以點(diǎn)這里查看: 策略模式:網(wǎng)絡(luò)小說(shuō)的固定套路 了解。
排序類中持有一個(gè) Comparator 接口的引用:
Comparator<? super K> comparator;
而我們可以傳入各種自定義排序規(guī)則的 Comparator 實(shí)現(xiàn)類,對(duì)同樣的類制定不同的排序策略。
總結(jié)
Java 中的兩種排序方式:
Comparable 自然排序。(實(shí)體類實(shí)現(xiàn))
Comparator 是定制排序。(無(wú)法修改實(shí)體類時(shí),直接在調(diào)用方創(chuàng)建)
同時(shí)存在時(shí)采用 Comparator(定制排序)的規(guī)則進(jìn)行比較。
對(duì)于一些普通的數(shù)據(jù)類型(比如 String, Integer, Double…),它們默認(rèn)實(shí)現(xiàn)了Comparable 接口,實(shí)現(xiàn)了 compareTo 方法,我們可以直接使用。
而對(duì)于一些自定義類,它們可能在不同情況下需要實(shí)現(xiàn)不同的比較策略,我們可以新創(chuàng)建 Comparator 接口,然后使用特定的 Comparator 實(shí)現(xiàn)進(jìn)行比較。
這就是 Comparable 和 Comparator 的區(qū)別。
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
- Java后臺(tái)通過(guò)Collections獲取list集合中最大數(shù),最小數(shù)代碼
- Java集合框架Collections原理及用法實(shí)例
- Java Collections集合繼承結(jié)構(gòu)圖_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
- JAVA對(duì)list集合進(jìn)行排序Collections.sort()
- JavaSE的三大接口:Comparator,Comparable和Cloneable詳解
- Java Comparable及Comparator接口區(qū)別詳解
- Java Comparable和Comparator對(duì)比詳解
- Java 比較接口comparable與comparator區(qū)別解析
- 淺析Java中comparator接口與Comparable接口的區(qū)別
- 對(duì)比Java中的Comparable排序接口和Comparator比較器接口
- java?集合工具類Collections及Comparable和Comparator排序詳解
相關(guān)文章
關(guān)于springBoot yml文件的list讀取問(wèn)題總結(jié)(親測(cè))
這篇文章主要介紹了關(guān)于springBoot yml文件的list讀取問(wèn)題總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12springboot模塊里面調(diào)用另外一個(gè)模塊的方法實(shí)現(xiàn)
在Spring-Boot項(xiàng)目開(kāi)發(fā)中,存在著本模塊的代碼需要訪問(wèn)外面模塊接口,本文就來(lái)介紹一下springboot模塊里面調(diào)用另外一個(gè)模塊的方法實(shí)現(xiàn),感興趣的可以了解一下2023-11-11Fluent Mybatis零xml配置實(shí)現(xiàn)復(fù)雜嵌套查詢
本文主要介紹了Fluent Mybatis零xml配置實(shí)現(xiàn)復(fù)雜嵌套查詢,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08Java Predicate及Consumer接口函數(shù)代碼實(shí)現(xiàn)解析
這篇文章主要介紹了Java Predicate及Consumer接口函數(shù)代碼實(shí)現(xiàn)解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06IDEA修改生成jar包名字的兩種方法實(shí)現(xiàn)
本文主要介紹了IDEA修改生成jar包名字的兩種方法實(shí)現(xiàn),通過(guò)簡(jiǎn)單的步驟,您可以修改項(xiàng)目名稱并在打包時(shí)使用新的名稱,具有一定的參考價(jià)值,感興趣的可以了解下2023-08-08Java?精煉解讀數(shù)據(jù)結(jié)構(gòu)的順序表如何操作
程序中經(jīng)常需要將一組數(shù)據(jù)元素作為整體管理和使用,需要?jiǎng)?chuàng)建這種元素組,用變量記錄它們,傳進(jìn)傳出函數(shù)等。一組數(shù)據(jù)中包含的元素個(gè)數(shù)可能發(fā)生變化,順序表則是將元素順序地存放在一塊連續(xù)的存儲(chǔ)區(qū)里,元素間的順序關(guān)系由它們的存儲(chǔ)順序自然表示2022-03-03Mybatis多參數(shù)及實(shí)體對(duì)象傳遞實(shí)例講解
在使用Mybatis的時(shí)候,經(jīng)常會(huì)有各種各樣的參數(shù)傳遞,不同類型,不同個(gè)數(shù)的參數(shù),下面小編通過(guò)例子給大家講解下Mybatis多參數(shù)及實(shí)體對(duì)象傳遞,一起看看吧2016-12-12SpringBoot關(guān)于自動(dòng)注入mapper為空的坑及解決
這篇文章主要介紹了SpringBoot關(guān)于自動(dòng)注入mapper為空的坑及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07