關于對象值相等的比較
三種比較風格
- 比較身份:==,通過等號來比較身份
- 比較值:通過使用equals方法,它是Object這個祖宗類所提供的的一個方法,也可以自己寫個類,重寫equals,自定制比較值的規則。
- 這里的equals需要通過用戶手動重寫才能夠按照值比較,換句話說就是你要是想比較值的話,就要約定好你到底按照什么樣的規則來進行比較,因為如果是比較身份的話,編譯器比較清楚,可以比較引用的地址,但如果是比較值的話,你一個類中有很多屬性,到底按哪些規則來比較,誰重要誰不重要,編譯器自己也不知道,這就需要用戶根據業務場景來指定按照哪些字段進行比較。
- 如果equals沒有手動重寫,默認執行的就是Object版本中的equals,此時的比較規則也是在比較身份。
- 比較類型: 通過instanceof,在類型轉型之前先比較一下,看看當前引用的真實類型到底是什么,以免出現類型轉換異常。
驗證== 和 equas在比較風格上的區別
代碼實現:按照身份來比較
package java2021_1018; //寫一個輔助的類Card(撲克牌-之前寫過) class Card{ //按照點數來比較 public String rank;//點數 public String suit;//花色 //提供構造方法 public Card(String rank, String suit) { this.rank = rank; this.suit = suit; } } public class TestCompare { public static void main(String[] args) { //寫一個類方法,在類方法中構造出這兩張牌 Card a=new Card("3","♠");//第一張牌:a的點數為1,花色為黑桃 Card b=new Card("3","♠");//第二張牌:b的點數為1,花色為黑桃 Card c=a; //此時已經有了三張牌,但是這三張牌來的方式并不一樣,a是通過new一個Card對象, //b也是是通過new一個Card對象,而c是相當于和直接a指向同一個card對象 //《使用 == 等號,進行比較》 System.out.println("==============使用 == 等號,進行比較的結果=============="); System.out.println(a == c);//結果為true System.out.println(a == b);//結果為false //第二個結果為true是因為a和b分別new了兩個對象,這兩個對象的身份是不相等的,所以執行 == 比較就是false的情況 //《使用equals進行比較》 System.out.println("==============使用equals進行比較的結果=============="); System.out.println(a.equals(c));//結果為true System.out.println(a.equals(b));//結果為false /*此時發現當前使用的equals和 == 結果一樣,沒什么區別,這說明當前這個equals你沒有對他進行重寫的時候,它就仍然是按照身份的方式進行比較的; * 所以,如果equals沒有手動重寫,默認執行的就是Object版本中的equals,此時的比較規則也是在比較身份。*/ } }
打印結果:
覆寫基類的equal
給equals加上一個重寫,使它變成比較值(內容)的方法
代碼實現:按照值來比較
package java2021_1018; class Card{ public String rank;//點數 public String suit;//花色 public Card(String rank, String suit) { this.rank = rank; this.suit = suit; } @Override public boolean equals(Object obj) {//參數:obj,類型:Object //按照值來比較this和obj //1.考慮自己和自己比較的情況 if(this == obj){ //先看看比較的這兩個對象是不是同一個對象,如果是,就返回true return true;//因為兩個對象(引用)如果身份相同的話,那么值也肯定是相同的 } //2.考慮obj為null的情況,認為結果為false, 避免出現空引用異常。 因為this是不可能為null的,如果它是null,就無法調用equals方法了 //所以如果obj為null,this不為null,那就返回false,有了這樣的一個條件判斷,就可以保證在后面這個obj == null邏輯執行的時候,就不會出現空引用異常了 if(obj == null){ return false; } //3、考慮類型不匹配的情況,即考慮obj這個類型是不是當前的Card類型,如果equals里傳了一個其他參數類型進來,此時兩個類型不同是無法比較相等的,所以需要判斷一下哦 if (!(obj instanceof Card)){//如果obj這個參數不是Card這個類型的話,就返回false return false; //同時類型轉換也帶有類型轉換失敗的風險,所以在使用之前也要先確認好類型是否匹配 } //4.真正的比較內容 Card other = (Card)obj;//此時的參數類型是Object類型,所以需要先對obj進行一個類型的強轉,并賦值給一個變量other //再去比較判斷 點數或花色 或 點數和花色 是否相等 return this.rank.equals(other.rank) && this.suit.equals(other.suit); } //這相當于是一個標準的重寫equals的一個模板,以后再寫其他的一些自己的比較方法的時候也要按照這種思路一步步往下考慮 } public class TestCompare1 { public static void main(String[] args) { Card a=new Card("3","♠");//第一張牌:a的點數為1,花色為黑桃 Card b=new Card("3","♠");//第二張牌:b的點數為1,花色為黑桃 Card c=a; System.out.println("==============使用 == 等號,進行比較的結果=============="); System.out.println(a == c);//結果為true System.out.println(a == b);//結果為true,因為a和b分別new了兩個對象,這兩個對象的身份是不相等的,所以執行 == 比較就是false的情況 System.out.println("==============使用equals進行比較的結果=============="); System.out.println(a.equals(c));//結果為true System.out.println(a.equals(b));//結果為true } }
打印結果:
注意:一般覆寫 equals 的套路就是上面演示的
在實現equals中所需考慮的幾個步驟,及涉及到的細節,在實現其他類的equals時也基本上就是代碼中所考慮的這幾個操作(套路)。
- 如果指向同一個對象,返回 true
- 如果傳入的為 null,返回 false
- 如果傳入的對象類型不是 Card,返回 false
- 按照類的實現目標完成比較,例如這里只要花色和數值一樣,就認為是相同的牌
- 注意下調用其他引用類型的比較也需要 equals,例如這里的 suit 的比較
- 覆寫基類equal的方式雖然可以比較,但缺陷是:equal只能按照相等進行比較,不能按照大于、小于的方式進行比較。
關于對象值大于、等于、小于的比較C基于自然順序(按照<小于號的形式)
基于Comparble接口類的比較
Comparble這個接口相當于就是重新定義小于這個操作
下面通過代碼來體會一下Comparble這個接口的作用,還是基于Card這個類來進行比較,如果想要使用Comparble這個接口的話,就需要讓Card實現一個Comparble,由于Comparble是一個帶泛型的接口,于是就需要給它寫一個泛型參數,但也不是非寫不可。
代碼實現:基于Comparble接口類的比較
package java2021_1018; class Card implements Comparable<Card>{//實現一個 Comparable的接口,由于Comparble是一個帶泛型的接口,于是就需要給它寫一個泛型參數 //按照點數來比較 public String rank;//點數 public String suit;//花色 //提供構造方法 public Card(String rank, String suit) { this.rank = rank; this.suit = suit; } //重寫compareTo方法 @Override public int compareTo(Card o) { if(o == null){//如果o傳過來的參數是一個空引用,就認為this比null要大 //一般也認為null的值比較小 return 1; } //點數的取值:是2~10的一系列整數,和J Q K A;如果點數值在2~10的話,直接返回成整數,如果點數值是J Q K A的話,就手動把這四個點數設置成11,12,13,14然后把值算出來之后再去分別比較大小 int rank1 = this.getValue();//this的值 int rank2 = o.getValue();//o的值 return rank1-rank2;//返回二者的差值,返回的值參考compareTo的語法規則 } //寫一個獲取值的方法, private int getValue() { //通過這個方法把String 類型的rank(J Q K A)變成數字點數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);//把字符串轉成數字 //單獨處理J Q K A,然后其他的2~10直接使用Integer.parseInt,把字符串轉換成整數。 } return value; } public class TestCompare1 { public static void main(String[] args) { //寫一個類方法,在類方法中構造出這兩張牌 Card a=new Card("3","♠");//第一張牌:a的點數為1,花色為黑桃 Card b=new Card("2","♦");//第二張牌:b的點數為1,花色為黑桃 Card c=a; //調用compareTo比較方法 System.out.println(a.compareTo(b)); System.out.println(a.compareTo(c)); } }
compareTo方法的語法規則
- 如果認為this 比 o 小,就返回一個<0的整數
- 如果認為this 比 o 大,就返回一個>0的整數
- 如果認為this 比 o 相等,就返回0
不同的點數比較后所打印出來的結果如下圖:
關于對象值大于、等于、小于的比較-- 基于比較器比較
基于Comparator接口類的比較
Comparator也是一個接口,想要使用它也要讓你的類去實現這個接口,同時去重寫一個compare方法,但是不同的是compare方法里面有兩個參數,
Comparable和Comparator,他倆的區別在于Comparator定義出的比較器和原來的類不是一個耦合在一起的關系
即使用Comparable的時候,你必須讓要比較的類實現Comparable接口,換句話說,就是需要修改這個類的代碼,比如剛才待比較類是Card,如果要是用Comparable這種實現方式的時候,必須得修改Card的源碼。
而使用Comparator的時候,你是重新創建一個新的類實現Comparator接口,不需要修改待比較類的代碼,比如剛才待比較類是Card,如果要是用Comparator這種實現方式的時候,就不用改Card的源碼
所以說使用Comparable這種實現方式的時候,它的耦合性就更強一些,但這是我們不愿意看到的。
代碼實現:基于Comparator接口類的比較
package java2021_1018; import java.util.Comparator; //寫一個輔助的類Card(撲克牌-之前寫過) class Card { //按照點數來比較 public String rank;//點數 public String suit;//花色 //提供構造方法 public Card(String rank, String suit) { this.rank = rank; this.suit = suit; } //寫一個獲取值的方法, public int getValue() { //通過這個方法把String 類型的rank(J Q K A)變成數字點數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);//把字符串轉成數字 //單獨處理J Q K A,然后其他的2~10直接使用Integer.parseInt,把字符串轉換成整數。 } return value; } }z //寫一個CardComparator類 class CatdComparator implements Comparator<Card>{//這里指定泛型參數就是針對誰比較就寫誰 //實現一個compare方法 @Override public int compare(Card o1, Card o2) {//compare方法里面有兩個參數,類型都是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) { //寫一個類方法,在類方法中構造出這兩張牌 Card a=new Card("3","♠");//第一張牌:a的點數為1,花色為黑桃 Card b=new Card("K","♠");//第二張牌:b的點數為1,花色為黑桃 Card c=a; //Comparator的使用:先創建一個Comparator的實例 CatdComparator comparator = new CatdComparator(); System.out.println(comparator.compare(a,b)); } }
疑問:為什么有了Comparable還需要有一個Comparator呢?
1、因為Comparable使用的時候必須要修改待比較類的代碼,實際開發中不是所有的類都能修改源碼,(如果這個類是庫或者是其他組的人提供的此時就不能隨便改人家的代碼,只能改自己的代碼)。
2、Comparable只能定義一種比較規則,Comparator可以定義多種比較規則(即可以實現多個Comparator類)。如:代碼中的CardComparator類,定義多份之后就可以有多種不同的比較規則了,然后就可以在不同的比較規則里面分別針對當前的場景來自定制按照什么樣的方式來比較了。
三種比較方式對比
覆寫的方法 | 說明 |
---|---|
Object.equals | 因為所有類都是繼承自 Object 的,所以直接覆寫即可,不過只能比較相等與否 |
Comparable.compareTo | 需要手動實現接口,侵入性比較強,但一旦實現,每次用該類都有順序,屬于內部順序 |
Comparator.compare | 需要實現一個比較器對象,對待比較類的侵入性弱,但對算法代碼實現侵入性 |
到此這篇關于java中對象的比較equal、Comparble、Comparator的區別的文章就介紹到這了,更多相關java equal、Comparble、Comparator內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://blog.csdn.net/m0_59157222/article/details/120945438