在Java中,凡是涉及到比較的,可以分為兩類情況:一類是基本數據類型的比較,另一類是引用數據類型的比較。對于基本數據類型的比較,我們通過關系運算符(==、>、<、!=、>=、<=)進行它們之間的比較,而對于引用數據類型,并不能簡單的通過關系運算符來進行它們的比較,因為引用數據類型如果使用關系運算符比較的話,比較的是它們的引用類型,并不是對象的內容。
本文就引用數據類型如何進行比較展開討論。
1.對象比較存在的問題
來看下面的例子:
class Card {public int rank; //點數public String suit; //花色public Card(int rank, String suit) {this.rank = rank;this.suit = suit;}
}public class Test {public static void main(String[] args) {Card card1 = new Card(1,"方塊");Card card2 = new Card(2,"方塊");Card card3 = card1;System.out.println(card1 > card2); //1.System.out.println(card1 == card2); //2.System.out.println(card2 < card1); //3.System.out.println(card1 == card3); //4.}
}
對于代碼中的1.、2.、3.和4.語句,1.和3.不能通過編譯,2.和4.可以通過遍歷,并且運行結果分別是:false(因為card1和card2是不同的對象)、true(card1和card3是相同的對象)。
面對這個結果,我們易產生疑問:為什么Java中引用類型的變量不能直接按照<或者>進行比較,為什么==就可以呢?
這是因為:對于我們用戶實現的自定義類型,都默認繼承Object類,而Object類中提供了equals方法,而==默認情況下調用equals方法,但是該方法的比較規則并沒有比較引用變量引用對象的內容,而是直接比較引用變量的地址!
但是有些情況下,我們需要比較的是引用變量引用對象的內容,比如:向優先級隊列中插入某個對象時,需要比較對象的內容來進行調整堆,那這又該怎么辦呢?
2.對象比較的方式
對于上述所說的需要比較對象內容的情況,再根據具體的比較需要,有三種方案,它們分別是:
重寫父類的equals方法、實現Comparable接口和使用比較器Comparator接口。
2.1 重寫父類的equals方法
該方案的核心用途:判斷兩個對象的內容是否 “邏輯相等”,是最基礎的對象內容比較方式。
適用場景:一般的對象內容比較(如判斷兩個Person
是否為同一個人)
在上述的例子中,我們我們認為兩張牌的點數和花色一樣,就認為它們是同一張牌,那么重寫equals方法,再進行測試:
package demo1;import java.util.Objects;class Card {public int rank; //點數public String suit; //花色public Card(int rank, String suit) {this.rank = rank;this.suit = suit;}@Overridepublic boolean equals(Object o) {//自己和自己比較if (this == o) {return true;}//如果是null對象或者o不是不是Card的子類if (o == null || getClass() != o.getClass()) {return false;}Card card = (Card) o;//比較點數和花色(花色是String類型,因此調用String的equals方法進行比較)return rank == card.rank && Objects.equals(suit, card.suit);}}public class Test {public static void main(String[] args) {Card card1 = new Card(1,"方塊");Card card2 = new Card(1,"方塊");Card card3 = card1;System.out.println(card1.equals(card2));System.out.println(card1.equals(card3));}
}//運行結果
true
true
代碼解讀:
- 如果指向同一個對象,返回 true
- 如果傳入的為 null,返回 false
- 如果傳入的對象類型不是 Card,返回 false
- 按照類的實現目標完成比較,例如這里只要花色和數值一樣,就認為是相同的牌
- 注意下調用其他引用類型的比較也需要 equals,例如這里的 suit 的比較
注意:重寫父類equal的方式雖然可以比較,但缺陷是:equal只能按照相等進行比較,不能按照大于、小于的方式進行比較,也就是說只能比較兩個東西是不是同一個。
2.2 實現Comparable接口
該方案的核心用途:為類定義 “默認排序規則”,讓對象自身具備可比較性。
適用場景:對象有明確的 “自然順序”(如學生按學號排序、商品按價格排序),且排序規則相對固定。
Comparable是JDK提供的泛型的比較接口類,它的源碼如下:
public interface Comparable<E> {
????????// 返回值:
????????// < 0: 表示 this 指向的對象小于 o 指向的對象
?????????// == 0: 表示 this 指向的對象等于 o 指向的對象
?????????// > 0: 表示 this 指向的對象大于 o 指向的對象
}
對于我們用戶自定義類型,如果想要按照大小進行比較時:在定義類時,實現Comparable接口即可,然后在類中重寫CompareTo方法。像這樣:
class Card implements Comparable<Card>{public int rank; //點數public String suit; //花色public Card(int rank, String suit) {this.rank = rank;this.suit = suit;}//不在意花色,進行點數的比較@Overridepublic int compareTo(Card o) {if (o == null) {return 1;}return rank - o.rank;}
}
public class Test {public static void main(String[] args) {Card card1 = new Card(2,"梅花");Card card2 = new Card(3,"方塊");Card card3 = new Card(2,"方塊");System.out.println(card1.compareTo(card2)); // < 0,說明card1小于card2System.out.println(card1.compareTo(card3)); //== 0,說明card1等于card3System.out.println(card2.compareTo(card3)); // > 0,說明card2大于card3}
}//運行結果
-1
0
1
【注意】Comparable是Java.lang中的接口類,可以直接使用。
2.3 使用比較器Comparator接口
該方案的核心用途:為類定義 “臨時 / 額外的排序規則”,不修改原類代碼。
適用場景:
- 需要多種排序規則(如
Person
既可以按年齡排,也可以按姓名排); - 無法修改原類代碼(如第三方庫的類);
- 優先級隊列(
PriorityQueue
)的元素排序(建大根堆需要)。
按照比較器方式進行比較,具體的操作如下:
1.用戶自定義比較器類,實現Comparator接口
public interface Comparator {
????????// 返回值:
????????// < 0: 表示 o1 指向的對象小于 o2 指向的對象
????????// == 0: 表示 o1 指向的對象等于 o2 指向的對象
? ? ? ??// > 0: 表示 o1 指向的對象等于 o2 指向的對象
????????int compare(T o1, T o2);
}
2.重寫Comparator中的compare方法
舉個例子:
import java.util.Comparator;class Card {public int rank; //點數public String suit; //花色public Card(int rank, String suit) {this.rank = rank;this.suit = suit;}
}//自定義比較器類
class CardComparator implements Comparator<Card> {//重寫compare方法//依舊按照數值比較@Overridepublic int compare(Card o1, Card o2) {//如果兩張牌數值一樣if (o1.rank == o2.rank) {return 0;}//如果第一種牌為nullif (o1 == null) {return -1;}//如果第二張牌為nullif (o2 == null) {return 1;}//正常情況return o1.rank - o2.rank;}
}
public class Test {public static void main(String[] args) {Card card1 = new Card(1,"方塊");Card card2 = new Card(2,"方塊");Card card3 = new Card(1,"方塊");//定義比較器對象CardComparator cardComparator = new CardComparator();System.out.println(cardComparator.compare(card1,card3)); // == 0,說明兩張牌相等System.out.println(cardComparator.compare(card1,card2)); // < 0,說明card1小于card2System.out.println(cardComparator.compare(card2,card1)); // > 0,說明card2大于card1}
}//運行結果
0
-1
1
【注意】Comparator是java.util 包中的泛型接口類,使用時必須導入對應的包。
2.4 三種方式的對比
重寫的方法 | 說明 |
---|---|
Object.equals | 因為所有類都是繼承自 Object 的,所以直接覆寫即可,不過只能比較相等與 否 |
Comparable.compareTo | 需要手動實現接口,侵入性比較強,但一旦實現,每次用該類都有順序,屬于內部順序 |
Comparator.compare | 需要實現一個比較器對象,對待比較類的侵入性弱,但對算法代碼實現侵入性 強 |
到此,關于如何去比較引用數據類型變量這個問題已經得到解決,我們有三種方案,按需使用即可!感謝您的閱讀,如有錯誤,還請指出!謝謝!