HashMap循环数据的删除
# HashMap遍历时删除数据
# 问题代码
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("123", "123");
map.put("1231", "123");
//边遍历边移除的错误写法
for (String key : map.keySet()) {
if ("123".equals(key)) {
//调用map的remove方法
map.remove(key);
}
}
map.forEach((k, v) -> System.out.println(k + " ---- " + v));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
Exception in thread "main" java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextNode(HashMap.java:1469) at java.util.HashMap$KeyIterator.next(HashMap.java:1493) at com.kusch.ares.aatmp.Test.main(Test.java:15) Process finished with exit code 1
1
2
3
4
5
6可见出现了错误
ConcurrentModificationException
# 问题报错处源码分析
从上述报错信息可以进到java.util.HashMap$HashIterator.nextNode(HashMap.java:1469)
处查看其对应的方法:
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
//出现在这里,modeCount 和 expectedModCount 不相等了
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
modCount 用于标识map被修改过了几次,expectedModCount是在你开始迭代遍历集合的时候赋予的值,在上述循环遍历之前,modCount和expectedModCount的数字是相等的,但是一旦在遍历中途进行了removet操作,modCount就会增加,从而导致报错。
看一下remove方法:
@Override
public boolean remove(Object key, Object value) {
return removeNode(hash(key), key, value, true, true) != null;
}
1
2
3
4
2
3
4
final Node<K,V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
// ......
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
if (node instanceof TreeNode)
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
else if (node == p)
tab[index] = node.next;
else
p.next = node.next;
//看这行,map的remove方法会增加modeCount
++modCount;
--size;
afterNodeRemoval(node);
return node;
}
}
return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 正确姿势
public class Test {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("123", "123");
map.put("1231", "123");
//边遍历,边移除的正确写法
Set<Map.Entry<String, String>> entries = map.entrySet();
Iterator<Map.Entry<String, String>> iterator = entries.iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> next = iterator.next();
if ("123".equals(next.getKey())) {
//此处调用的是 迭代器 的remove方法
iterator.remove();
}
}
map.forEach((k, v) -> System.out.println(k + " ---- " + v));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
迭代器的remove为什么就没问题呢?看一下迭代器的remove代码:
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
//注意这里,移除之后,它更新了expectedModCount的值
expectedModCount = modCount;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
编辑 (opens new window)
上次更新: 2023/05/28, 08:23:00