equals常見面試題
在開始聊之前,我們先看幾個(gè)常見的面試題撵摆,看看你能不能都回答上來底靠。
- 1、equals和==有什么區(qū)別特铝?
- 2暑中、hashcode相等的兩個(gè)對(duì)象一定==相等嗎壹瘟?equals相等嗎?
- 3鳄逾、兩個(gè)對(duì)象用equals比較相等稻轨,那它們的hashcode相等嗎?
如果我們不重寫equals和hashcode雕凹,那么它使用的是Object方法的實(shí)現(xiàn)殴俱。我們先簡單看一下
public boolean equals(Object obj) {
return (this == obj);
}
public static int hashCode(Object o) {
return o != null ? o.hashCode() : 0;
}
為什么要重寫equals
通過以上代碼可以看出,Object提供的equals在進(jìn)行比較的時(shí)候枚抵,并不是進(jìn)行值比較线欲,而是內(nèi)存地址的比較。由此可以知曉汽摹,要使用equals對(duì)對(duì)象進(jìn)行比較李丰,那么就必須進(jìn)行重寫equals。
重寫equals不重寫hashCode會(huì)存在什么問題
我們先看下面這段話
每個(gè)覆蓋了equals方法的類中逼泣,必須覆蓋hashCode趴泌。如果不這么做,就違背了hashCode的通用約定拉庶,也就是上面注釋中所說的嗜憔。進(jìn)而導(dǎo)致該類無法結(jié)合所以與散列的集合一起正常運(yùn)作,這里指的是HashMap砍的、HashSet痹筛、HashTable、ConcurrentHashMap廓鞠。
來自 Effective Java 第三版
結(jié)論:如果重寫equals不重寫hashCode它與散列集合無法正常工作帚稠。
既然這樣那我們就拿我們最熟悉的HashMap來進(jìn)行演示推導(dǎo)吧。我們知道HashMap中的key是不能重復(fù)的床佳,如果重復(fù)添加滋早,后添加的會(huì)覆蓋前面的內(nèi)容。那么我們看看HashMap是如何來確定key的唯一性的砌们。
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
查看代碼發(fā)現(xiàn)杆麸,它是通過計(jì)算Map key的hashCode值來確定在鏈表中的存儲(chǔ)位置的。那么這樣就可以推測(cè)出浪感,如果我們重寫了equals但是沒重寫hashCode昔头,那么可能存在元素重復(fù)的矛盾情況。
下面我們來演示一下
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));
}
按正常情況來推測(cè)影兽,map中只存在兩個(gè)元素揭斧,employee2和employee3。
執(zhí)行結(jié)果
出現(xiàn)這種問題的原因就是因?yàn)闆]有重寫hashCode峻堰,導(dǎo)致map在計(jì)算key的hash值的時(shí)候讹开,絕對(duì)值相同的對(duì)象計(jì)算除了不一致的hash值盅视。
接下來我們打開hashCode的注釋代碼,看看執(zhí)行結(jié)果
總結(jié)
如果重寫了equals就必須重寫hashCode旦万,如果不重寫將引起與散列集合(HashMap闹击、HashSet、HashTable成艘、ConcurrentHashMap)的沖突赏半。