为什么在HashMap迭代期间,更改键/值对的值不会抛出ConcurrentModificationException?

时间:2016-12-08 17:58:29

标签: java hashmap iterator

例如,请参阅以下代码段:

        Map<String,String> unsafemap=new HashMap<>();
        unsafemap.put("hello",null);
        unsafemap.put(null, null);
        unsafemap.put("world","hello");
        unsafemap.put("foo","hello");
        unsafemap.put("bar","hello");
        unsafemap.put("john","hello");
        unsafemap.put("doe","hello");

        System.out.println("changing null values");

                for(Iterator<Map.Entry<String,String>> i=unsafemap.entrySet().iterator();i.hasNext();){

                    Map.Entry<String,String> e=i.next();

                    System.out.println("key : "+e.getKey()+" value :"+e.getValue());
                    if(e.getValue() == null){

                        //why is the below line not throwing ConcurrentModificationException
                        unsafemap.put(e.getKey(), "no data");

                        //same result, no ConcurrentModificationException thrown
                        e.setValue("no data");
                        }

                    //throws ConcurrentModificationException
                    unsafemap.put("testKey","testData");

                }
                System.out.println("---------------------------------");
                for(Map.Entry<String,String> e :unsafemap.entrySet()){
                    System.out.println(e);
                }

在迭代期间修改映射总是会导致异常,如果没有使用迭代器完成,例如iterator.remove()。因此,显然在迭代期间添加一个新值会抛出异常,但是如果修改了特定键/值对的值,为什么不抛出它?

1 个答案:

答案 0 :(得分:0)

Entry对象在您的第一种情况下已经存在,因此将使用e.value = value;修改该值并返回,并且不会生成任何新条目。所以,这里也不例外。

在第二种情况下,对值对象所做的更改实际上不会影响地图,因此没有例外。

来自HashMap源代码:

public V put(K key, V value) {
     if (key == null)
         return putForNullKey(value);
     int hash = hash(key.hashCode());
     int i = indexFor(hash, table.length);
     for (Entry<K,V> e = table[i]; e != null; e = e.next) {
         Object k;
         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
             V oldValue = e.value;
             e.value = value;
             e.recordAccess(this);
             return oldValue;
         }
     }

     modCount++;
     addEntry(hash, key, value, i);
     return null;
 }