【问题标题】:Adding elements to an HashMap concurrently同时向 HashMap 添加元素
【发布时间】:2017-09-09 09:17:09
【问题描述】:

如果我迭代标准 HashMap 并尝试在迭代时向其中添加元素,我会得到 ConcurrentModificationException

所以我尝试了一个允许并发添加的 HashMap:

ConcurrentHashMap<String, Integer> cMap = new ConcurrentHashMap<>();
cMap.put("one", 1);
cMap.forEach((key, value) -> cMap.put(key + key, value + value));
System.out.println(cMap);

但是,生成的地图有点奇怪:

 {oneoneoneoneoneoneoneone=8, one=1, oneone=2, oneoneoneone=4}

如果将密钥更改为 zx (cMap.put("zx", 1)),现在的结果是:

{zxzx=2, zx=1}

问题:

1) 为什么会这样?两个并发操作(迭代和添加)不应冲突。

2) 不一致如何解决?

与集合相反,在迭代字符串的字符时更改字符串时,不会遇到此问题:

        String str = scanner.next();
        for (int i = 1; i < str.length(); i++) {
            if (str.charAt(i) == str.charAt(i-1)) {
                str = str.substring(0, i-1) + str.substring(i+1);
                i = 0;
            }
        }
        if (str.length() == 0) {
            System.out.println("Empty String");
        } else {
            System.out.println (str);
        }
    }

请注意,在上述循环中,源字符串实际上并未更改,而是重新分配,因为字符串是不可变的,无法修改

上面的代码运行良好且始终如一。这是为什么字符串是线程安全的一个例子吗?

【问题讨论】:

  • 知道,我看过那个例子somewhere...
  • @Holger 你在一个本地 Java 组中被引用了你在那里制作的那些 cmets(导致OOM),我们有时在这里做 :) 再次阅读它真是太棒了

标签: java string multithreading collections concurrency


【解决方案1】:

归结为添加新元素的哈希桶。在您的第一个示例中,您的新元素被添加到比您正在处理的哈希桶更晚的哈希桶中,并且迭代器尚未到达。在您的第二个示例中,您的新元素被添加到迭代器已经读取的早期哈希桶中。

您应该非常小心在迭代过程中修改集合。在您的情况下,您最好将新条目添加到新地图中,然后将它们合并在一起。

【讨论】:

  • 这不仅仅是“最有可能”。您实际上可以在输出中看到哈希桶顺序:首先添加了one,迭代器读取它,代码添加了oneone,它最终在后面的桶中,迭代器读取它,代码添加了oneoneoneone,它最终在一个更晚的桶,迭代器读取它,代码添加了oneoneoneoneoneoneoneone,它最终在one之前的一个桶中,所以迭代器结束。地图的默认大小意味着在这些小示例中不会发生重新散列。
猜你喜欢
  • 1970-01-01
  • 2019-07-25
  • 1970-01-01
  • 2011-12-13
  • 1970-01-01
  • 1970-01-01
  • 2012-05-31
  • 2014-05-01
  • 1970-01-01
相关资源
最近更新 更多