如何修改HashMap中的鍵?
1. 概述
在Java中, HashMap是一種廣泛使用的數據結構,它以鍵值對的形式存儲元素,提供數據的快速訪問和檢索。有時,在使用HashMap時,我們可能想要修改現有條目的鍵。
在本教程中,我們將探討如何在 Java 中修改HashMap中的鍵。
2.使用remove()然後put()
首先我們看一下HashMap是如何存儲鍵值對的。 HashMap uses Node類型來維護鍵值對:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
...
}
正如我們所看到的, key聲明有final關鍵字。因此,在將鍵對象放入 HashMap 後,我們無法重新分配它**HashMap .**
雖然我們不能簡單地替換一個key,但是我們仍然可以通過其他方式達到我們預期的結果。接下來,讓我們從另一個角度來看我們的問題。
假設HashMap.中有一個條目K1 -> V現在,我們想將K1更改為K2 ,以獲得K2 -> V.事實上,實現這一點的最簡單的想法是找到“K1”的條目並將鍵“K1”替換為“K2”.但是,我們也可以刪除K1 -> V關聯並添加新的K2 -> V條目。
Map接口提供了remove(key)方法,可以通過鍵從地圖中刪除條目。此外, remove()方法返回從map中刪除的value 。
接下來,讓我們通過一個例子來看看這種方法是如何工作的。為簡單起見,我們將使用單元測試斷言來驗證結果是否符合我們的預期:
Map<String, Integer> playerMap = new HashMap<>();
playerMap.put("Kai", 42);
playerMap.put("Amanda", 88);
playerMap.put("Tom", 200);
上面的簡單代碼顯示了一個HashMap,其中保存了一些玩家姓名( String )及其得分( Integer )的關聯。接下來,我們將條目“Kai” -> 42中的玩家名“ Kai ”替換為“ Eric ”:
// replace Kai with Eric
playerMap.put("Eric", playerMap.remove("Kai"));
assertFalse(playerMap.containsKey("Kai"));
assertTrue(playerMap.containsKey("Eric"));
assertEquals(42, playerMap.get("Eric"));
我們可以看到,單行語句playerMap.put(“Eric”, playerMap.remove(“Kai”));做兩件事。它刪除帶有鍵“ Kai ”的條目,獲取其值 ( 42 ),並添加一個新條目“Eric” -> 42 。
當我們運行測試時,它通過了。因此,這種方法按預期工作。
雖然我們的問題已經解決了,但還有一個潛在的問題。我們知道HashMap的 key 是一個final變量。因此,我們無法重新分配變量。但我們可以修改final對象的值。嗯,在我們的playerMap示例中,鍵是String.我們無法更改它的值,因為String是不可變的。但是如果是可變對象,我們可以通過修改key來解決問題嗎?
接下來我們就來了解一下。
3. 永遠不要修改HashMap中的鍵
首先,我們不應該使用可變對像作為 Java 中HashMap鍵,這可能會導致潛在的問題和意外的行為。
這是因為**HashMap中的鍵對像用於計算哈希碼,該哈希碼決定了存儲相應值的存儲桶。**如果鍵是可變的並且在用作HashMap中的鍵後發生更改,則哈希碼也會更改。因此,我們將無法正確檢索與該鍵關聯的值,因為它將位於錯誤的存儲桶中。
接下來我們通過一個例子來理解一下。
首先,我們創建一個只有一個屬性的Player類:
class Player {
private String name;
public Player(String name) {
this.name = name;
}
// getter and setter methods are omitted
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Player)) {
return false;
}
Player player = (Player) o;
return name.equals(player.name);
}
@Override
public int hashCode() {
return name.hashCode();
}
}
正如我們所看到的, Player類在name屬性上有一個 setter。所以,它是可變的。此外, hashCode()方法使用name屬性計算哈希碼。這意味著更改Player對象的名稱可以使其具有不同的哈希代碼。
接下來,讓我們創建一個地圖並在其中放置一些條目,使用Player對像作為鍵:
Map<Player, Integer> myMap = new HashMap<>();
Player kai = new Player("Kai");
Player tom = new Player("Tom");
Player amanda = new Player("Amanda");
myMap.put(kai, 42);
myMap.put(amanda, 88);
myMap.put(tom, 200);
assertTrue(myMap.containsKey(kai));
接下來,我們將玩家kai的name從“Kai”改為“Eric”,然後驗證是否能得到預期的結果:
//change Kai's name to Eric
kai.setName("Eric");
assertEquals("Eric", kai.getName());
Player eric = new Player("Eric");
assertEquals(eric, kai);
// now, the map contains neither Kai nor Eric:
assertFalse(myMap.containsKey(kai));
assertFalse(myMap.containsKey(eric));
正如上面的測試所示,**將kai的name更改為“ Eric ”後,我們無法再使用kai或 eric 檢索條目“ Eric ” -> 42 eric.**然而,對象Player(“Eric”)作為鍵存在於地圖中:
// although the Player("Eric") exists:
long ericCount = myMap.keySet()
.stream()
.filter(player -> player.getName()
.equals("Eric"))
.count();
assertEquals(1, ericCount);
要理解為什麼會發生這種情況,我們首先需要了解HashMap是如何工作的。
HashMap維護一個內部哈希表,用於在將鍵添加到映射時存儲鍵的哈希碼。哈希碼引用映射條目。例如,當我們使用get(key)方法檢索條目時, HashMap會計算給定鍵對象的哈希碼並在哈希表中查找哈希碼。
在上面的示例中,我們將kai(“Kai”)放入地圖中。因此,哈希碼是根據字符串“Kai”. HashMap將結果(比方說“hash-kai”,存儲在哈希表中。後來,我們把kai(“Kai”)改為kai(“Eric”).當我們嘗試通過kai(“Eric”), HashMap計算“hash-eric”作為哈希碼。然後,它在哈希表中查找。當然,它不會找到它。
不難想像,如果我們在實際應用程序中這樣做,則可能很難找到這種意外行為的根本原因。
因此,我們不應該使用可變對像作為HashMap中的鍵。此外,我們永遠不應該修改密鑰。
4。結論
在本文中,我們學習了“ remove() then put()”方法來替換HashMap.此外,我們通過一個示例討論了為什麼我們應該避免使用可變對像作為HashMap中的鍵,以及為什麼我們永遠不應該修改HashMap中的鍵。
與往常一樣,示例的完整源代碼可在 GitHub 上獲取。