細說 equals 和==的那些事
1,起源于 Object
我們都知道 Java 中所有的類都預設繼承了 Object 這個超類,其中 Object 有 10 多個方法,這裡我們介紹一下主要的
toString
,
equals
和
hashcode
方法。
-
方法預設傳回“toSting
”;我們一般對它進行重寫,然後就可直接列印對象得到輸出重寫後的形式。類名@位址值
下面重點介紹一下
equals
和
hashcode
:
-
方法預設比較對象(位址),即進行的是==運算。如果需要比較内容,需要進行重寫。equals
-
預設是根據對象位址傳回一串整數,也就是說位址相同hashcode
就相同。而且hashcode
是 Object 中的hashcode
方法,本地方法,表明該方法事調用了别的語言實作的,有點像接口,不用自己實作。native
下面是 Object 類中的相關源碼:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
public boolean equals(Object obj) {
return (this == obj);
}
2,糾纏在 String
我們再來說一下
java.lang.String
中
equals
和
hashcode
的特點:
String 對
equals
和
hashcode
都進行了重寫。
首先 hashcode 是根據對象内容計算 hash,是以對象内容相同,他們的 hashcode 就相同。
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
//這種 hash 計算方式不難看出,元素 value 相同 h 會是一樣的
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
對于 string 而言,equlas 比較的是對象内容,這也是由于字元串的特殊性。
那麼==比較的就是對象位址。
public boolean equals(Object anObject) {
//對象相同,直接相同
if (this == anObject) {
return true;
}
//類型相同且每個元素值相同,認為相同
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
3,代碼驗證
下面是測試代碼:
public class TEST {
public static void main(String[] args){
Object o1 = new Object();
Object o2 = new Object();
Object o3 = o2;
System.out.println(o1==o2); //false
System.out.println(o1.equals(o2)); //false,Object 原生 equals,預設比較位址
System.out.println(o1.hashCode()==o2.hashCode()); //false,根據位址計算 hash
System.out.println(o2.hashCode()==o3.hashCode()); //true
String s1 = "abc";
String s2 = s1; //常量池優化機制
String s3 = new String("abc");
System.out.println(s1==s3); //false 位址不一樣,一個在常量池中一個在堆中
System.out.println(s1.equals(s3)); //true,比較内容
System.out.println(s2.hashCode()==s1.hashCode()); //true,根據内容計算 hash
System.out.println(s1.hashCode()==s3.hashCode()); //true
}
}
4,重寫方法
有些場景中,我們需要自定義”相等“規則。比如我們定義對象的内容相同即為相等,此時需要重寫 equals 方法:
import java.util.Objects;
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object o) {
// 如果對象位址一樣,則認為相同
if (this == o)
return true;
// 如果參數為空,或者類型資訊不一樣,則認為不同
if (o == null || getClass() != o.getClass())
return false;
// 轉換為目前類型
Person person = (Person) o;
// 要求基本類型相等,并且将引用類型交給 java.util.Objects 類的 equals 靜态方法取用結果
return age == person.age && Objects.equals(name, person.name);
}
}
//對象比較位址,字元串比較内容
public static boolean equals(Object a, Object b) {
//1,對象類型時
//a==b 為 true,則 true
//a==b 為 false,則 false;
//2,string 類型時
//位址 true,内容 true,則 true
//位址 false,内容 true,則 true;位址不等内容不等,flase
return (a == b) || (a != null && a.equals(b));
}
在有些場合,我們還需要重寫 hashcode,比如 HashSet 中“元素相等”的判斷機制就是綜合考慮了 hashcode 和 equals 方法;是以當我們自定義對象時需要重寫 hashcode 和 equals 兩個方法。
equals 的方法重寫參考上例,下面是 hashcode 方法的重寫以及相應方法源碼:
@Override
public int hashCode() {
//仍然調用的是 Objects 的 hash 方法
//對于這裡而言,age 走位址判斷;name 走内容判斷;是以隻要他們内容相同就會傳回相同的 hash
return Objects.hash(name, age);
}
public static int hash(Object... values) {
return Arrays.hashCode(values);
}
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
//仍然走的是 Object 的 hashCode 方法
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
當然上述重寫方法,idea 的 Generate 提供了重寫模闆,可一鍵生成。
5,總結
Object 的 hashcode 根據位址傳回值,equals 比較位址,==比較位址;
String 的 hashcode 根據内容傳回值,equals 比較内容,==比較位址。
即 Object 一律比較的是位址,由于 String 進行了方法重寫,都是比較的内容。==其實本身就是比較位址。