3 回答

TA貢獻(xiàn)1806條經(jīng)驗(yàn) 獲得超5個(gè)贊
HashMap使用hashCode(),==并equals()用于條目查找。給定鍵的查找序列k如下:
使用k.hashCode()來(lái)確定條目存儲(chǔ)其斗,如果有的話
如果找到,對(duì)于k1該桶中的每個(gè)條目的密鑰,如果k == k1 || k.equals(k1),則返回k1的條目
任何其他結(jié)果,沒(méi)有相應(yīng)的條目
為了演示一個(gè)例子,假設(shè)我們想要?jiǎng)?chuàng)建一個(gè)HashMapwhere鍵,如果它們具有相同的整數(shù)值(由AmbiguousIntegerclass 表示),則它們?cè)谶壿嬌鲜堑葍r(jià)的。然后我們構(gòu)造一個(gè)HashMap,放入一個(gè)條目,然后嘗試覆蓋其值并按鍵檢索值。
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
}
HashMap<AmbiguousInteger, Integer> map = new HashMap<>();
// logically equivalent keys
AmbiguousInteger key1 = new AmbiguousInteger(1),
key2 = new AmbiguousInteger(1),
key3 = new AmbiguousInteger(1);
map.put(key1, 1); // put in value for entry '1'
map.put(key2, 2); // attempt to override value for entry '1'
System.out.println(map.get(key1));
System.out.println(map.get(key2));
System.out.println(map.get(key3));
Expected: 2, 2, 2
不要覆蓋hashCode()和equals():在默認(rèn)情況下的Java生成不同的hashCode()不同對(duì)象的值,因此HashMap使用這些值映射key1和key2成不同的桶。key3沒(méi)有相應(yīng)的桶,所以它沒(méi)有價(jià)值。
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 2, set as entry 2[1]
map.get(key1); // map to bucket 1, get as entry 1[1]
map.get(key2); // map to bucket 2, get as entry 2[1]
map.get(key3); // map to no bucket
Expected: 2, 2, 2
Output: 1, 2, null
hashCode()僅覆蓋: HashMap映射key1并key2進(jìn)入同一個(gè)存儲(chǔ)桶,但由于兩者key1 == key2和key1.equals(key2)檢查失敗,它們保持不同的條目,因?yàn)槟J(rèn)情況下equals()使用==check,它們引用不同的實(shí)例。key3失敗都==和equals()檢查對(duì)key1和key2,因此具有沒(méi)有對(duì)應(yīng)的值。
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
@Override
public int hashCode() {
return value;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 1, set as entry 1[2]
map.get(key1); // map to bucket 1, get as entry 1[1]
map.get(key2); // map to bucket 1, get as entry 1[2]
map.get(key3); // map to bucket 1, no corresponding entry
Expected: 2, 2, 2
Output: 1, 2, null
equals()僅覆蓋: HashMap由于默認(rèn)值不同,將所有密鑰映射到不同的存儲(chǔ)桶hashCode()。==或者equals()檢查在這里是無(wú)關(guān)緊要的,因?yàn)樗麳ashMap永遠(yuǎn)不會(huì)達(dá)到需要使用它們的程度。
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
@Override
public boolean equals(Object obj) {
return obj instanceof AmbiguousInteger && value == ((AmbiguousInteger) obj).value;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 2, set as entry 2[1]
map.get(key1); // map to bucket 1, get as entry 1[1]
map.get(key2); // map to bucket 2, get as entry 2[1]
map.get(key3); // map to no bucket
Expected: 2, 2, 2
Actual: 1, 2, null
覆蓋both hashCode()和equals():HashMapmaps key1,key2并覆蓋key3到同一個(gè)存儲(chǔ)桶中。==在比較不同的實(shí)例時(shí)檢查失敗,但是equals()檢查通過(guò),因?yàn)樗鼈兌季哂邢嗤闹?,并且被我們的邏輯視為“邏輯上等效”?/p>
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
@Override
public int hashCode() {
return value;
}
@Override
public boolean equals(Object obj) {
return obj instanceof AmbiguousInteger && value == ((AmbiguousInteger) obj).value;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 1, set as entry 1[1], override value
map.get(key1); // map to bucket 1, get as entry 1[1]
map.get(key2); // map to bucket 1, get as entry 1[1]
map.get(key3); // map to bucket 1, get as entry 1[1]
Expected: 2, 2, 2
Actual: 2, 2, 2
如果hashCode()隨機(jī)怎么辦?:HashMap將為每個(gè)操作分配一個(gè)不同的存儲(chǔ)桶,因此您永遠(yuǎn)不會(huì)找到您之前輸入的相同條目。
class AmbiguousInteger {
private static int staticInt;
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
@Override
public int hashCode() {
return ++staticInt; // every subsequent call gets different value
}
@Override
public boolean equals(Object obj) {
return obj instanceof AmbiguousInteger && value == ((AmbiguousInteger) obj).value;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 2, set as entry 2[1]
map.get(key1); // map to no bucket, no corresponding value
map.get(key2); // map to no bucket, no corresponding value
map.get(key3); // map to no bucket, no corresponding value
Expected: 2, 2, 2
Actual: null, null, null
如果hashCode()總是一樣的話怎么辦?:HashMap將所有鍵映射到一個(gè)大桶中。在這種情況下,您的代碼在功能上是正確的,但使用HashMap實(shí)際上是多余的,因?yàn)槿魏螜z索都需要在O(N)時(shí)間內(nèi)迭代該單個(gè)存儲(chǔ)桶中的所有條目(或Java 8的O(logN)),等效使用a List。
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
@Override
public int hashCode() {
return 0;
}
@Override
public boolean equals(Object obj) {
return obj instanceof AmbiguousInteger && value == ((AmbiguousInteger) obj).value;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 1, set as entry 1[1]
map.get(key1); // map to bucket 1, get as entry 1[1]
map.get(key2); // map to bucket 1, get as entry 1[1]
map.get(key3); // map to bucket 1, get as entry 1[1]
Expected: 2, 2, 2
Actual: 2, 2, 2
如果equals總是假的怎么辦?:==當(dāng)我們將同一個(gè)實(shí)例與自身進(jìn)行比較時(shí)檢查通過(guò),但是否則失敗,equals檢查總是失敗key1,key2并且key3被認(rèn)為是“邏輯上不同”,并且映射到不同的條目,盡管它們?nèi)匀辉谕煌爸衕ashCode()。
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
@Override
public int hashCode() {
return 0;
}
@Override
public boolean equals(Object obj) {
return false;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 1, set as entry 1[2]
map.get(key1); // map to bucket 1, get as entry 1[1]
map.get(key2); // map to bucket 1, get as entry 1[2]
map.get(key3); // map to bucket 1, no corresponding entry
Expected: 2, 2, 2
Actual: 1, 2, null
好的,如果equals現(xiàn)在總是如此?:你基本上是說(shuō)所有對(duì)象都被認(rèn)為與另一個(gè)對(duì)象“在邏輯上等同”,所以它們都映射到同一個(gè)桶(由于相同hashCode()),相同的條目。
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
@Override
public int hashCode() {
return 0;
}
@Override
public boolean equals(Object obj) {
return true;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 1, set as entry 1[1], override value
map.put(new AmbiguousInteger(100), 100); // map to bucket 1, set as entry1[1], override value
map.get(key1); // map to bucket 1, get as entry 1[1]
map.get(key2); // map to bucket 1, get as entry 1[1]
map.get(key3); // map to bucket 1, get as entry 1[1]
Expected: 2, 2, 2
Actual: 100, 100, 100

TA貢獻(xiàn)1803條經(jīng)驗(yàn) 獲得超6個(gè)贊
你已經(jīng)超越equals而沒(méi)有覆蓋hashCode。您必須確保equals對(duì)于兩個(gè)對(duì)象返回true的所有情況,hashCode返回相同的值。哈希碼是一個(gè)代碼,如果兩個(gè)對(duì)象相等則必須相等(反之不必為真)。當(dāng)您將硬編碼值設(shè)置為9時(shí),您再次滿足合同。
在哈希映射中,僅在哈希桶中測(cè)試相等性。你的兩個(gè)星期一對(duì)象應(yīng)該是相同的,但是因?yàn)樗鼈兎祷夭煌墓4a,所以equals甚至不調(diào)用該方法來(lái)確定它們的相等性 - 它們被直接放入不同的桶中,甚至不考慮它們相等的可能性。

TA貢獻(xiàn)2011條經(jīng)驗(yàn) 獲得超2個(gè)贊
我不能強(qiáng)調(diào)你應(yīng)該閱讀Effective Java中的第3章(警告:pdf鏈接)。在該章中,您將了解有關(guān)覆蓋方法的所有信息Object,特別是有關(guān)equals合同的信息。喬什布洛赫有一個(gè)很好的方法來(lái)覆蓋equals你應(yīng)該遵循的方法。它將幫助您了解您應(yīng)該使用的原因,equals而不是==您equals方法的特定實(shí)現(xiàn)。
希望這可以幫助。請(qǐng)仔細(xì)閱讀。(至少是前幾個(gè)項(xiàng)目......然后你會(huì)想要閱讀其余的內(nèi)容:-)。
添加回答
舉報(bào)