java中對象的比較equal、Comparble、Comparator的區(qū)別
關(guān)于對象值相等的比較
三種比較風(fēng)格
- 比較身份:==,通過等號來比較身份
- 比較值:通過使用equals方法,它是Object這個(gè)祖宗類所提供的的一個(gè)方法,也可以自己寫個(gè)類,重寫equals,自定制比較值的規(guī)則。
- 這里的equals需要通過用戶手動(dòng)重寫才能夠按照值比較,換句話說就是你要是想比較值的話,就要約定好你到底按照什么樣的規(guī)則來進(jìn)行比較,因?yàn)槿绻潜容^身份的話,編譯器比較清楚,可以比較引用的地址,但如果是比較值的話,你一個(gè)類中有很多屬性,到底按哪些規(guī)則來比較,誰重要誰不重要,編譯器自己也不知道,這就需要用戶根據(jù)業(yè)務(wù)場景來指定按照哪些字段進(jìn)行比較。
- 如果equals沒有手動(dòng)重寫,默認(rèn)執(zhí)行的就是Object版本中的equals,此時(shí)的比較規(guī)則也是在比較身份。
- 比較類型: 通過instanceof,在類型轉(zhuǎn)型之前先比較一下,看看當(dāng)前引用的真實(shí)類型到底是什么,以免出現(xiàn)類型轉(zhuǎn)換異常。
驗(yàn)證== 和 equas在比較風(fēng)格上的區(qū)別
代碼實(shí)現(xiàn):按照身份來比較
package java2021_1018;
//寫一個(gè)輔助的類Card(撲克牌-之前寫過)
class Card{
//按照點(diǎn)數(shù)來比較
public String rank;//點(diǎn)數(shù)
public String suit;//花色
//提供構(gòu)造方法
public Card(String rank, String suit) {
this.rank = rank;
this.suit = suit;
}
}
public class TestCompare {
public static void main(String[] args) {
//寫一個(gè)類方法,在類方法中構(gòu)造出這兩張牌
Card a=new Card("3","♠");//第一張牌:a的點(diǎn)數(shù)為1,花色為黑桃
Card b=new Card("3","♠");//第二張牌:b的點(diǎn)數(shù)為1,花色為黑桃
Card c=a;
//此時(shí)已經(jīng)有了三張牌,但是這三張牌來的方式并不一樣,a是通過new一個(gè)Card對象,
//b也是是通過new一個(gè)Card對象,而c是相當(dāng)于和直接a指向同一個(gè)card對象
//《使用 == 等號,進(jìn)行比較》
System.out.println("==============使用 == 等號,進(jìn)行比較的結(jié)果==============");
System.out.println(a == c);//結(jié)果為true
System.out.println(a == b);//結(jié)果為false
//第二個(gè)結(jié)果為true是因?yàn)閍和b分別new了兩個(gè)對象,這兩個(gè)對象的身份是不相等的,所以執(zhí)行 == 比較就是false的情況
//《使用equals進(jìn)行比較》
System.out.println("==============使用equals進(jìn)行比較的結(jié)果==============");
System.out.println(a.equals(c));//結(jié)果為true
System.out.println(a.equals(b));//結(jié)果為false
/*此時(shí)發(fā)現(xiàn)當(dāng)前使用的equals和 == 結(jié)果一樣,沒什么區(qū)別,這說明當(dāng)前這個(gè)equals你沒有對他進(jìn)行重寫的時(shí)候,它就仍然是按照身份的方式進(jìn)行比較的;
* 所以,如果equals沒有手動(dòng)重寫,默認(rèn)執(zhí)行的就是Object版本中的equals,此時(shí)的比較規(guī)則也是在比較身份。*/
}
}
打印結(jié)果:

覆寫基類的equal
給equals加上一個(gè)重寫,使它變成比較值(內(nèi)容)的方法
代碼實(shí)現(xiàn):按照值來比較
package java2021_1018;
class Card{
public String rank;//點(diǎn)數(shù)
public String suit;//花色
public Card(String rank, String suit) {
this.rank = rank;
this.suit = suit;
}
@Override
public boolean equals(Object obj) {//參數(shù):obj,類型:Object
//按照值來比較this和obj
//1.考慮自己和自己比較的情況
if(this == obj){ //先看看比較的這兩個(gè)對象是不是同一個(gè)對象,如果是,就返回true
return true;//因?yàn)閮蓚€(gè)對象(引用)如果身份相同的話,那么值也肯定是相同的
}
//2.考慮obj為null的情況,認(rèn)為結(jié)果為false, 避免出現(xiàn)空引用異常。 因?yàn)閠his是不可能為null的,如果它是null,就無法調(diào)用equals方法了
//所以如果obj為null,this不為null,那就返回false,有了這樣的一個(gè)條件判斷,就可以保證在后面這個(gè)obj == null邏輯執(zhí)行的時(shí)候,就不會(huì)出現(xiàn)空引用異常了
if(obj == null){
return false;
}
//3、考慮類型不匹配的情況,即考慮obj這個(gè)類型是不是當(dāng)前的Card類型,如果equals里傳了一個(gè)其他參數(shù)類型進(jìn)來,此時(shí)兩個(gè)類型不同是無法比較相等的,所以需要判斷一下哦
if (!(obj instanceof Card)){//如果obj這個(gè)參數(shù)不是Card這個(gè)類型的話,就返回false
return false;
//同時(shí)類型轉(zhuǎn)換也帶有類型轉(zhuǎn)換失敗的風(fēng)險(xiǎn),所以在使用之前也要先確認(rèn)好類型是否匹配
}
//4.真正的比較內(nèi)容
Card other = (Card)obj;//此時(shí)的參數(shù)類型是Object類型,所以需要先對obj進(jìn)行一個(gè)類型的強(qiáng)轉(zhuǎn),并賦值給一個(gè)變量other
//再去比較判斷 點(diǎn)數(shù)或花色 或 點(diǎn)數(shù)和花色 是否相等
return this.rank.equals(other.rank) && this.suit.equals(other.suit);
}
//這相當(dāng)于是一個(gè)標(biāo)準(zhǔn)的重寫equals的一個(gè)模板,以后再寫其他的一些自己的比較方法的時(shí)候也要按照這種思路一步步往下考慮
}
public class TestCompare1 {
public static void main(String[] args) {
Card a=new Card("3","♠");//第一張牌:a的點(diǎn)數(shù)為1,花色為黑桃
Card b=new Card("3","♠");//第二張牌:b的點(diǎn)數(shù)為1,花色為黑桃
Card c=a;
System.out.println("==============使用 == 等號,進(jìn)行比較的結(jié)果==============");
System.out.println(a == c);//結(jié)果為true
System.out.println(a == b);//結(jié)果為true,因?yàn)閍和b分別new了兩個(gè)對象,這兩個(gè)對象的身份是不相等的,所以執(zhí)行 == 比較就是false的情況
System.out.println("==============使用equals進(jìn)行比較的結(jié)果==============");
System.out.println(a.equals(c));//結(jié)果為true
System.out.println(a.equals(b));//結(jié)果為true
}
}
打印結(jié)果:

注意:一般覆寫 equals 的套路就是上面演示的
在實(shí)現(xiàn)equals中所需考慮的幾個(gè)步驟,及涉及到的細(xì)節(jié),在實(shí)現(xiàn)其他類的equals時(shí)也基本上就是代碼中所考慮的這幾個(gè)操作(套路)。
- 如果指向同一個(gè)對象,返回 true
- 如果傳入的為 null,返回 false
- 如果傳入的對象類型不是 Card,返回 false
- 按照類的實(shí)現(xiàn)目標(biāo)完成比較,例如這里只要花色和數(shù)值一樣,就認(rèn)為是相同的牌
- 注意下調(diào)用其他引用類型的比較也需要 equals,例如這里的 suit 的比較
- 覆寫基類equal的方式雖然可以比較,但缺陷是:equal只能按照相等進(jìn)行比較,不能按照大于、小于的方式進(jìn)行比較。
關(guān)于對象值大于、等于、小于的比較–基于自然順序(按照<小于號的形式)
基于Comparble接口類的比較
Comparble這個(gè)接口相當(dāng)于就是重新定義小于這個(gè)操作
下面通過代碼來體會(huì)一下Comparble這個(gè)接口的作用,還是基于Card這個(gè)類來進(jìn)行比較,如果想要使用Comparble這個(gè)接口的話,就需要讓Card實(shí)現(xiàn)一個(gè)Comparble,由于Comparble是一個(gè)帶泛型的接口,于是就需要給它寫一個(gè)泛型參數(shù),但也不是非寫不可。

代碼實(shí)現(xiàn):基于Comparble接口類的比較
package java2021_1018;
class Card implements Comparable<Card>{//實(shí)現(xiàn)一個(gè) Comparable的接口,由于Comparble是一個(gè)帶泛型的接口,于是就需要給它寫一個(gè)泛型參數(shù)
//按照點(diǎn)數(shù)來比較
public String rank;//點(diǎn)數(shù)
public String suit;//花色
//提供構(gòu)造方法
public Card(String rank, String suit) {
this.rank = rank;
this.suit = suit;
}
//重寫compareTo方法
@Override
public int compareTo(Card o) {
if(o == null){//如果o傳過來的參數(shù)是一個(gè)空引用,就認(rèn)為this比null要大
//一般也認(rèn)為null的值比較小
return 1;
}
//點(diǎn)數(shù)的取值:是2~10的一系列整數(shù),和J Q K A;如果點(diǎn)數(shù)值在2~10的話,直接返回成整數(shù),如果點(diǎn)數(shù)值是J Q K A的話,就手動(dòng)把這四個(gè)點(diǎn)數(shù)設(shè)置成11,12,13,14然后把值算出來之后再去分別比較大小
int rank1 = this.getValue();//this的值
int rank2 = o.getValue();//o的值
return rank1-rank2;//返回二者的差值,返回的值參考compareTo的語法規(guī)則
}
//寫一個(gè)獲取值的方法,
private int getValue() {
//通過這個(gè)方法把String 類型的rank(J Q K A)變成數(shù)字點(diǎn)數(shù)11,12,13,14
int value = 0;
if("J".equals(rank)){
value = 11;
}else if("Q".equals(rank)){
value = 12;
}else if("K".equals(rank)){
value = 13;
}else if ("A".equals(rank)){
value = 14;
}else {
value = Integer.parseInt(rank);//把字符串轉(zhuǎn)成數(shù)字
//單獨(dú)處理J Q K A,然后其他的2~10直接使用Integer.parseInt,把字符串轉(zhuǎn)換成整數(shù)。
}
return value;
}
public class TestCompare1 {
public static void main(String[] args) {
//寫一個(gè)類方法,在類方法中構(gòu)造出這兩張牌
Card a=new Card("3","♠");//第一張牌:a的點(diǎn)數(shù)為1,花色為黑桃
Card b=new Card("2","♦");//第二張牌:b的點(diǎn)數(shù)為1,花色為黑桃
Card c=a;
//調(diào)用compareTo比較方法
System.out.println(a.compareTo(b));
System.out.println(a.compareTo(c));
}
}
compareTo方法的語法規(guī)則
- 如果認(rèn)為this 比 o 小,就返回一個(gè)<0的整數(shù)
- 如果認(rèn)為this 比 o 大,就返回一個(gè)>0的整數(shù)
- 如果認(rèn)為this 比 o 相等,就返回0
不同的點(diǎn)數(shù)比較后所打印出來的結(jié)果如下圖:

關(guān)于對象值大于、等于、小于的比較-- 基于比較器比較
基于Comparator接口類的比較
Comparator也是一個(gè)接口,想要使用它也要讓你的類去實(shí)現(xiàn)這個(gè)接口,同時(shí)去重寫一個(gè)compare方法,但是不同的是compare方法里面有兩個(gè)參數(shù),
Comparable和Comparator,他倆的區(qū)別在于Comparator定義出的比較器和原來的類不是一個(gè)耦合在一起的關(guān)系
即使用Comparable的時(shí)候,你必須讓要比較的類實(shí)現(xiàn)Comparable接口,換句話說,就是需要修改這個(gè)類的代碼,比如剛才待比較類是Card,如果要是用Comparable這種實(shí)現(xiàn)方式的時(shí)候,必須得修改Card的源碼。
而使用Comparator的時(shí)候,你是重新創(chuàng)建一個(gè)新的類實(shí)現(xiàn)Comparator接口,不需要修改待比較類的代碼,比如剛才待比較類是Card,如果要是用Comparator這種實(shí)現(xiàn)方式的時(shí)候,就不用改Card的源碼
所以說使用Comparable這種實(shí)現(xiàn)方式的時(shí)候,它的耦合性就更強(qiáng)一些,但這是我們不愿意看到的。
代碼實(shí)現(xiàn):基于Comparator接口類的比較
package java2021_1018;
import java.util.Comparator;
//寫一個(gè)輔助的類Card(撲克牌-之前寫過)
class Card {
//按照點(diǎn)數(shù)來比較
public String rank;//點(diǎn)數(shù)
public String suit;//花色
//提供構(gòu)造方法
public Card(String rank, String suit) {
this.rank = rank;
this.suit = suit;
}
//寫一個(gè)獲取值的方法,
public int getValue() {
//通過這個(gè)方法把String 類型的rank(J Q K A)變成數(shù)字點(diǎn)數(shù)11,12,13,14
int value = 0;
if("J".equals(rank)){
value = 11;
}else if("Q".equals(rank)){
value = 12;
}else if("K".equals(rank)){
value = 13;
}else if ("A".equals(rank)){
value = 14;
}else {
value = Integer.parseInt(rank);//把字符串轉(zhuǎn)成數(shù)字
//單獨(dú)處理J Q K A,然后其他的2~10直接使用Integer.parseInt,把字符串轉(zhuǎn)換成整數(shù)。
}
return value;
}
}z
//寫一個(gè)CardComparator類
class CatdComparator implements Comparator<Card>{//這里指定泛型參數(shù)就是針對誰比較就寫誰
//實(shí)現(xiàn)一個(gè)compare方法
@Override
public int compare(Card o1, Card o2) {//compare方法里面有兩個(gè)參數(shù),類型都是Card類型
//判斷特殊情況
if(o1 == o2){//如果o1 和 o2身份相等
return 0;
}
//判斷o1 、 o2是不是null的情況
if(o1 == null){
return -1;
}
if(o2 == null){
return 1;
}
//比較值
int value1 = o1.getValue();
int value2 = o2.getValue();
return value1-value2;
}
}
public class TestCompare1 {
public static void main(String[] args) {
//寫一個(gè)類方法,在類方法中構(gòu)造出這兩張牌
Card a=new Card("3","♠");//第一張牌:a的點(diǎn)數(shù)為1,花色為黑桃
Card b=new Card("K","♠");//第二張牌:b的點(diǎn)數(shù)為1,花色為黑桃
Card c=a;
//Comparator的使用:先創(chuàng)建一個(gè)Comparator的實(shí)例
CatdComparator comparator = new CatdComparator();
System.out.println(comparator.compare(a,b));
}
}
疑問:為什么有了Comparable還需要有一個(gè)Comparator呢?
1、因?yàn)镃omparable使用的時(shí)候必須要修改待比較類的代碼,實(shí)際開發(fā)中不是所有的類都能修改源碼,(如果這個(gè)類是庫或者是其他組的人提供的此時(shí)就不能隨便改人家的代碼,只能改自己的代碼)。
2、Comparable只能定義一種比較規(guī)則,Comparator可以定義多種比較規(guī)則(即可以實(shí)現(xiàn)多個(gè)Comparator類)。如:代碼中的CardComparator類,定義多份之后就可以有多種不同的比較規(guī)則了,然后就可以在不同的比較規(guī)則里面分別針對當(dāng)前的場景來自定制按照什么樣的方式來比較了。
三種比較方式對比
| 覆寫的方法 | 說明 |
|---|---|
| Object.equals | 因?yàn)樗蓄惗际抢^承自 Object 的,所以直接覆寫即可,不過只能比較相等與否 |
| Comparable.compareTo | 需要手動(dòng)實(shí)現(xiàn)接口,侵入性比較強(qiáng),但一旦實(shí)現(xiàn),每次用該類都有順序,屬于內(nèi)部順序 |
| Comparator.compare | 需要實(shí)現(xiàn)一個(gè)比較器對象,對待比較類的侵入性弱,但對算法代碼實(shí)現(xiàn)侵入性 |
到此這篇關(guān)于java中對象的比較equal、Comparble、Comparator的區(qū)別的文章就介紹到這了,更多相關(guān)java equal、Comparble、Comparator內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MybatisPlus使用idworker解決雪花算法重復(fù)
本文主要介紹了MybatisPlus使用idworker解決雪花算法重復(fù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02
eclipse啟動(dòng)一個(gè)Springboot項(xiàng)目
本文主要介紹了eclipse啟動(dòng)一個(gè)Springboot項(xiàng)目,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
解決dubbo啟動(dòng)報(bào)服務(wù)注冊失敗Failed?to?register?dubbo
這篇文章主要介紹了解決dubbo啟動(dòng)報(bào)服務(wù)注冊失敗Failed?to?register?dubbo問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
VS?Code中運(yùn)行Java?SpringBoot的項(xiàng)目詳細(xì)步驟
這篇文章主要介紹了VS?Code中運(yùn)行Java?SpringBoot項(xiàng)目的相關(guān)資料,文中涵蓋了安裝必要的擴(kuò)展、配置環(huán)境、創(chuàng)建或?qū)腠?xiàng)目、配置調(diào)試環(huán)境、運(yùn)行和調(diào)試項(xiàng)目、使用Spring?Boot?Actuator以及配置任務(wù)自動(dòng)化等步驟,需要的朋友可以參考下2024-12-12
Java中用戶向系統(tǒng)傳遞參數(shù)的三種基本方式實(shí)例分享
這篇文章主要介紹了Java中用戶向系統(tǒng)傳遞參數(shù)的三種基本方式實(shí)例,有需要的朋友可以參考一下2014-01-01
Mybatis generator如何自動(dòng)生成代碼
這篇文章主要介紹了Mybatis generator如何自動(dòng)生成代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12
使用feign服務(wù)調(diào)用添加Header參數(shù)
這篇文章主要介紹了使用feign服務(wù)調(diào)用添加Header參數(shù)的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
Java Stream中的Spliterator類概念及原理解析
Spliterator是Java 8引入的一個(gè)接口,位于java.util包中,它結(jié)合了迭代器(Iterator)的遍歷能力和分割器(Splitter)的分割能力,本文將詳細(xì)介紹Spliterator的概念、原理、作用、類中定義的關(guān)鍵方法,以及它在Stream API中的實(shí)際應(yīng)用,感興趣的朋友一起看看吧2024-08-08

