【问题标题】:Replace/Changing HashMap items during iteration在迭代期间替换/更改 HashMap 项
【发布时间】:2012-01-08 10:08:57
【问题描述】:

当我尝试从 HashMap 中删除项目时,我得到并发修改异常。我知道在迭代期间通过 HashMap 删除项目会触发此异常,但我需要用新项目替换旧项目。我怎样才能做到这一点 ?也许要创建 countNumberOfEachCharacter HashMap 的副本,并遍历原始 HashMap 以从副本 HashMap 中删除项目?

countNumberOfEachCharacter = new HashMap<Character,Character>();
if (countNumberOfEachCharacter.containsKey(word.charAt(i))) {
    System.out.println("This character already exists");                     
    for (Iterator it = countNumberOfEachCharacter.entrySet().iterator(); it.hasNext();) {

      Map.Entry entry = (Map.Entry) it.next();

      Object key = entry.getKey();
      Object value = entry.getValue();

      if (key.equals(word.charAt(i))) { 

        int toIncrease = Integer.parseInt(value.toString());
        toIncrease++;

        System.out.println("key  "+key);                                                     
        System.out.println("increased  "+toIncrease);                                                       
        countNumberOfEachCharacter.remove(word.charAt(i));

        char c = Character.forDigit(toIncrease, 10);                                                     
        countNumberOfEachCharacter.put(word.charAt(i),c);                                                                                                                                                                               
    }                                                                                                                                                                                                           
  }                                                                           
}
else {    

   System.out.println("First time found this character");

   char c = Character.forDigit(1, 10);                                
   countNumberOfEachCharacter.put(word.charAt(i),c);                            
   System.out.println("Stored "+word.charAt(i)+" with count "+c);                                                               
}

【问题讨论】:

  • 请花点时间使问题中的代码更具可读性。
  • @Eng.Fouad 这不会解决问题,因为没有并发访问。他只是在迭代它并删除元素。
  • 这实际上可能是我见过的最糟糕的代码:您正在使用Character 在地图中存储一个数字,然后使用Integer.parseInt(Character.toString()),递增它,然后保存Character.forDigit(Integer, 10)回到地图中。你喜欢疼痛还是什么?只需存储一个Integer

标签: java hashmap


【解决方案1】:

在迭代 Collection 时,您只能使用 Iterator#remove 方法删除元素。这也记录在 HashMap 的类 javadoc 中

这个类的所有“集合视图”返回的迭代器 方法”是快速失败的:如果地图在任何结构上被修改 迭代器创建后的时间,除了通过 迭代器自己的remove方法,迭代器会抛出一个 并发修改异常。因此,面对并发 修改,迭代器快速而干净地失败,而不是 在不确定的时间冒着任意的、不确定的行为的风险 未来

此外,对于您正在尝试执行的操作(=更新值),您不必将其删除。只需使用该键和更新的值调用put,这将更新值,如HashMap#put 方法的javadoc 中所述

将指定的值与此映射中的指定键相关联。如果 映射先前包含键的映射,旧值是 换了。

【讨论】:

    【解决方案2】:

    ...但我需要用新的替换旧项目

    我从“替换”(以及您引用的代码)中得知 key 保持不变,只是值不同。如果是这样,我不相信在Map.Entry 对象上调用setValue 会导致ConcurrentModificationException,所以你可以这样做。

    更新:刚刚测试过,确实有效:

    import java.util.*;
    
    public class ReplaceMapEntryValue {
    
        public static final void main(String[] args) {
            Map m;
            Iterator<Map.Entry> it;
            Map.Entry entry;
    
            // Create
            m = new HashMap();
            m.put("a", "alpha");
            m.put("b", "beta");
    
            // Update
            it = m.entrySet().iterator();
            while (it.hasNext()) {
                entry = it.next();
                if (entry.getKey() == "b") {
                    entry.setValue("bravo");
                }
            }
    
            // Show
            it = m.entrySet().iterator();
            while (it.hasNext()) {
                entry = it.next();
                System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue());
            }
    
            // Done
            System.exit(0);
        }
    }
    

    【讨论】:

      【解决方案3】:

      地图的全部意义在于您可以按键查找,而不必检查每个条目。

      Map<Character, AtomicInteger> countNumberOfEachCharacter = new TreeMap<Character, AtomicInteger>();
      
      String word = "the quick brown fox jumps over the lazy dog";
      for (int i = 0; i < word.length(); i++) {
          AtomicInteger count = countNumberOfEachCharacter.get(word.charAt(i));
          if (count == null)
              countNumberOfEachCharacter.put(word.charAt(i), new AtomicInteger(1));
          else
              count.incrementAndGet();
      }
      System.out.println("Character count: " + countNumberOfEachCharacter);
      

      打印

      Character count: { =8, a=1, b=1, c=1, d=1, e=3, f=1, g=1, h=2, i=1, j=1, k=1, l=1, m=1, n=1, o=4, p=1, q=1, r=2, s=1, t=2, u=2, v=1, w=1, x=1, y=1, z=1}
      

      但是,由于您有少量固定数量的可能字符,您甚至不需要使用地图

      int[] countNumberOfEachCharacter = new int[Character.MAX_VALUE + 1];
      
      String word = "the quick brown fox jumps over the lazy dog";
      for (int i = 0; i < word.length(); i++)
          countNumberOfEachCharacter[word.charAt(i)]++;
      
      System.out.print("Character count: ");
      for (int i = 0; i < countNumberOfEachCharacter.length; i++)
          if (countNumberOfEachCharacter[i] > 0)
              System.out.print(" " + (char) i + "=" + countNumberOfEachCharacter[i]);
      System.out.println();
      

      打印

      Character count:   =8 a=1 b=1 c=1 d=1 e=3 f=1 g=1 h=2 i=1 j=1 k=1 l=1 m=1 n=1 o=4 p=1 q=1 r=2 s=1 t=2 u=2 v=1 w=1 x=1 y=1 z=1
      

      【讨论】:

        猜你喜欢
        • 2011-05-13
        • 2014-10-02
        • 2020-06-20
        • 2012-03-07
        • 2018-09-16
        • 1970-01-01
        • 2012-06-15
        • 1970-01-01
        相关资源
        最近更新 更多