
[英]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.