如何修改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 上獲取。