Kusch's blog Kusch's blog
首页
Java
框架
部署与运维
数据库
  • 我的电脑
  • 科学技巧
  • 杂文铺
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Mr.Kusch

入门前把门拆了的程序猿一枚
首页
Java
框架
部署与运维
数据库
  • 我的电脑
  • 科学技巧
  • 杂文铺
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java基础

  • Java集合

    • HashMap循环数据的删除
      • HashMap遍历时删除数据
        • 问题代码
        • 问题报错处源码分析
        • 正确姿势
  • JavaIO

  • Java并发

  • JVM

  • Java
  • Java集合
Mr.Kusch
2023-04-17
目录

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
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

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
 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

# 正确姿势

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

迭代器的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
编辑 (opens new window)
#随笔
上次更新: 2023/05/28, 08:23:00
重写equals和hashCode的必要性
常见的IO

← 重写equals和hashCode的必要性 常见的IO→

最近更新
01
Linux与Docker的时区
05-30
02
Docker部署Sentinel
05-30
03
Git提交常用表情 转载
05-28
更多文章>
Theme by Vdoing | Copyright © 2023-2023 Mr.Kusch | MIT License | 苏ICP备20030181号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式