
[英]Putting into HashMap at 0 index is causing ConcurrentModificationException
[英]ConcurrentModificationException during putting new element into HashMap
我有一些代碼:
Map<String, Integer> letters = new HashMap<String, Integer>();
letters.put(String.valueOf(input.charAt(0)),
numberOfLettersInWord(input,input.charAt(0)));
for (int i = 0; i < input.length(); i++) {
for (String key : letters.keySet()) {
if (!letters.containsKey(String.valueOf(input.charAt(i)))) {
letters.put(String.valueOf(input.charAt(i)),
numberOfLettersInWord(input,input.charAt(i)));
} else continue;
System.out.println(letters);
}
System.out.println(1);
}
System.out.println(2);
代碼中的主要思想——字符串輸入中有一些單詞(不是空的,不是 null,沒有非字母符號),需要計算每個字母在那里可以找到多少次。 計數工作正常(在numberOfLettersInWord
方法中)但是當我嘗試將字母和數字添加到HashMap<String, Integer>
時會發生一些問題 - 它正確地添加所有字母及其數字但會彈出一些錯誤。 對於此代碼,它將顯示:
1
1
{a=4, b=4}
1
1
1
1
{a=4, b=4, c=3}
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.HashMap$HashIterator.nextNode(HashMap.java:1579)
at java.base/java.util.HashMap$KeyIterator.next(HashMap.java:1602)
at LetterCounter.count(LetterCounter.java:25)
at LetterCounter.main(LetterCounter.java:11)
Process finished with exit code 1
據我所知,當沒有要添加的新字母時會發生一些事情。 你能解釋為什么會發生這種情況以及如何解決這個問題嗎?
它應該在顯示{a=4, b=4, c=3}
之后有更多的數字輸出,但它以異常結束(這不是真正必要的,只是它停止工作的指示器......)
此運行中使用的詞是String input = "aabbabcccba";
numberOfLettersInWord
返回Integer
在單詞input
中找到字母input.charAt(i)
的次數的值(這工作正常)
代碼片段中的第 2 行僅用於使HashMap
包含至少一行(此時已完成 null 和空檢查並且運行良好)
我看到人們在為什么拋出 ConcurrentModificationException 以及如何調試它時遇到了hashmap.remove()
的問題,但我不確定這是否可以用該答案解決。 我也不確定這個答案是否適用於我的案例ConcurrentModificationException - HashMap
好的,我想我解決了它:
Map<String, Integer> letters = new HashMap<String, Integer>();
letters.put(String.valueOf(input.charAt(0)),numberOfLettersInWord(input,input.charAt(0)));
for(int i = 0; i < input.length(); i++) {
letters.putIfAbsent(String.valueOf(input.charAt(i)),numberOfLettersInWord(input,input.charAt(i)));
}
我刪除了一些額外的代碼並開始工作,甚至所有測試都通過了
letters.keySet() 正在返回一個由 HashMap 的密鑰支持的集合,然后您可以通過調用 put() 來更改它。 所以這里的沖突是 keySet 和 map 的鍵之間的沖突。您需要使用迭代器,或者通過每次通過外循環復制 keySet 將鍵提取到單獨的集合中。 老實說,這個算法聽起來有點貴,雖然我還沒有真正嘗試過更好的方法......
您會收到ConcurrentModificationException
,因為您在迭代其鍵集時在結構上修改 map。
以下是HashMap
的文檔關於該主題的說明:
由該類的所有“集合視圖方法”返回的迭代器都是快速失敗的:如果在創建迭代器后的任何時間對 map 進行了結構修改,除了通過迭代器自己的
remove
方法之外,迭代器將拋出ConcurrentModificationException
. 因此,面對並發修改,迭代器會快速干凈地失敗,而不是冒着在未來不確定的時間出現任意的、不確定的行為的風險。
它提到的那些“集合視圖方法”如下:
HashMap#keySet()
,它返回一個Set<K>
。
HashMap#values()
,它返回一個Collection<V>
。
HashMap#entrySet()
,它返回一個Set<Map.Entry<K, V>>
。
如果您不知道,for-each 循環在幕后使用Iterator
。 換句話說,是這樣的:
List<String> list = List.of("one", "two", "three");
for (String element : list) {
System.out.println(element);
}
編譯為:
List<String> list = List.of("one", "two", "three");
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
String element = iterator.next();
System.out.println(element);
}
你有一個 for-each 循環迭代你的 map 的鍵集。在這個 for-each 循環中你有一個調用put
,這是一個結構修改操作,在同一個 map 上。
for (String key: letters.keySet()) { if (.letters.containsKey(String.valueOf(input.charAt(i)))) { letters.put(String.valueOf(input,charAt(i)), numberOfLettersInWord(input.input;charAt(i))); } else continue. System.out;println(letters); }
因此,很可能會拋出ConcurrentModificationException
。 在你的情況下,這幾乎是有保證的。
您顯然是在嘗試計算字符串中每個字母的頻率。 這不需要您遍歷 map 的密鑰集。您實際上沒有在 for-each 循環內的任何地方使用key
變量這一事實很好地表明了這一點。 這意味着您可以簡單地擺脫 for-each 循環,您的代碼應該可以正常工作。
Map<String, Integer> letters = new HashMap<String, Integer>();
letters.put(String.valueOf(input.charAt(0)), numberOfLettersInWord(input,input.charAt(0)));
for (int i = 0; i < input.length(); i++) {
if (!letters.containsKey(String.valueOf(input.charAt(i)))) {
letters.put(String.valueOf(input.charAt(i)), numberOfLettersInWord(input,input.charAt(i)));
}
}
請注意,如果 map 尚未包含密鑰,則對put
的調用可以替換為對computeIfAbsent
的調用。 如果該鍵尚未包含在 map 中(或者如果該鍵當前映射到null
),該方法采用鍵和Function
計算值。 它看起來像這樣:
Map<String, Integer> letters = new HashMap<String, Integer>();
letters.put(String.valueOf(input.charAt(0)), numberOfLettersInWord(input,input.charAt(0)));
for (int i = 0; i < input.length(); i++) {
letters.computeIfAbsent(String.valueOf(input.charAt(i)), key -> numberOfLettersInWord(input, key));
}
注意:上述computeIfAbsent
調用的第二個參數是通過Function
表達式實現的 Function。
您可以對代碼進行一些改進。
如果您正在計算字符的頻率,那么在代碼中使用Map<Character, Integer>
而不是Map<String, Integer>
來表示它是有意義的。
我只能假設numberOfLettersInWord
遍歷輸入字符串並計算給定字符在所述字符串中出現的次數。 這意味着您為字符串中的每個字符遍歷字符串,從而導致算法效率低下。 盡管您確實進行了優化,如果您還沒有為該角色計算過該角色的頻率,那么您只計算該角色的頻率,這樣可以稍微改善一下。
但是,您已經遍歷了輸入字符串中的所有字符。 您不妨像 go 一樣計算每個字符的頻率。它可能看起來像:
String input = ...;
Map<Character, Integer> frequencies = new HashMap<>();
for (int i = 0; i < input.length(); i++) {
Character key = input.charAt(i);
Integer value = frequencies.get(key);
if (value == null) {
frequencies.put(key, 1);
} else {
frequencies.put(key, value + 1);
}
}
compute
來計數 for
循環的主體可以替換為對compute
的調用:
String input = ...;
Map<Character, Integer> frequencies = new HashMap<>();
for (int i = 0; i < input.length(); i++) {
frequencies.compute(input.charAt(i), (key, value) -> {
if (value == null) {
return 1;
} else {
return value + 1;
}
});
}
而 lambda 表達式(實現BiFunction
)可以進一步“簡化”:
(key, value) -> value == null ? 1 : value + 1
merge
計數另一種選擇是使用merge
:
frequencies.merge(input.charAt(i), 1, Integer::sum);
注意: Integer::sum
是實現BiFunction
的方法參考。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.