equals常見面試題
在開始聊之前,我們先看幾個常見的面試題,看看你能不能都回答上來。
- 1、equals和==有什麼區別?
- 2、hashcode相等的兩個物件一定==相等嗎?equals相等嗎?
- 3、兩個物件用equals比較相等,那它們的hashcode相等嗎?
如果我們不重寫equals和hashcode,那麼它使用的是Object方法的實現。我們先簡單看一下
public boolean equals(Object obj) {
return (this == obj);
}
public static int hashCode(Object o) {
return o != null ? o.hashCode() : 0;
}
為什麼要重寫equals
通過以上程式碼可以看出,Object提供的equals在進行比較的時候,並不是進行值比較,而是記憶體地址的比較。由此可以知曉,要使用equals對物件進行比較,那麼就必須進行重寫equals。
重寫equals不重寫hashCode會存在什麼問題
我們先看下面這段話
每個覆蓋了equals方法的類中,必須覆蓋hashCode。如果不這麼做,就違背了hashCode的通用約定,也就是上面註釋中所說的。進而導致該類無法結合所以與雜湊的集合一起正常運作,這裡指的是HashMap、HashSet、HashTable、ConcurrentHashMap。
來自 Effective Java 第三版
結論:如果重寫equals不重寫hashCode它與雜湊集合無法正常工作。
既然這樣那我們就拿我們最熟悉的HashMap來進行演示推導吧。我們知道HashMap中的key是不能重複的,如果重複新增,後新增的會覆蓋前面的內容。那麼我們看看HashMap是如何來確定key的唯一性的。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
檢視程式碼發現,它是通過計算Map key的hashCode值來確定在連結串列中的儲存位置的。那麼這樣就可以推測出,如果我們重寫了equals但是沒重寫hashCode,那麼可能存在元素重複的矛盾情況。
下面我們來演示一下
public class Employee {
private String name;
private Integer age;
public Employee(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return Objects.equals(name, employee.name) &&
Objects.equals(age, employee.age);
}
/*@Override
public int hashCode() {
return Objects.hash(name, age);
}*/
}
public static void main(String[] args) {
Employee employee1 = new Employee("冰峰", 20);
Employee employee2 = new Employee("冰峰", 22);
Employee employee3 = new Employee("冰峰", 20);
HashMap<Employee, Object> map = new HashMap<>();
map.put(employee1, "1");
map.put(employee2, "1");
map.put(employee3, "1");
System.out.println("equals:" + employee1.equals(employee3));
System.out.println("hashCode:" + (employee1.hashCode() == employee3.hashCode()));
System.out.println(JSONObject.toJSONString(map));
}
按正常情況來推測,map中只存在兩個元素,employee2和employee3。
執行結果
出現這種問題的原因就是因為沒有重寫hashCode,導致map在計算key的hash值的時候,絕對值相同的物件計算除了不一致的hash值。
接下來我們開啟hashCode的註釋程式碼,看看執行結果
總結
如果重寫了equals就必須重寫hashCode,如果不重寫將引起與雜湊集合(HashMap、HashSet、HashTable、ConcurrentHashMap)的衝突。