java.util.ConcurrentModificationException 异常解决办法及原理(顶)

24 篇文章 0 订阅

最近在修程序的bug,发现后台抛出以下异常:

Exception in thread "main" java.util.ConcurrentModificationException
  at java.util.HashMap$HashIterator.nextEntry(HashMap.java:793)
  at java.util.HashMap$KeyIterator.next(HashMap.java:828)
  at com.keyman.demo.test.ClearResultTable.method2(ClearResultTable.java:54)
  at com.keyman.demo.test.ClearResultTable.main(ClearResultTable.java:88)

找到报错行:at com.keyman.demo.test.ClearResultTable.method2(ClearResultTable.java:54)发现,报错位置:for (String s1 : sets)

Set<String> sets = map.keySet();
    for (String s1 : sets) {
      String value = map.get(s1);
      // 删除满足value以abc开头的键值对
      if (value.startsWith("abc")) {
        map.remove(s1);
      }
    }

或者下面的方式同样也会抛出异常

Iterator<String> iterator = map.keySet().iterator();
    while (iterator.hasNext()) {
      String key = iterator.next();
      String value = map.get(key);
      // 删除满足value以abc开头的键值对
      if (value.startsWith("abc")) {
        map.remove(key); 
        //iterator.remove(); // 同步modCount和expectedModCount
        
      }
    }

其实不管是

Map 还是 Set

这样操作时均会抛出此异常!

解决办法为:如果不是Iterator迭代方式,则修改map迭代方式为Iterator()方式,采用iterator.remove();而不直接通过map.remove();

Iterator<String> iterator = map.keySet().iterator();
    while (iterator.hasNext()) {
      String key = iterator.next();
      String value = map.get(key);
      // 删除满足value以abc开头的键值对
      if (value.startsWith("abc")) {
        //map.remove(value); 
        iterator.remove();   // 关键代码,同步modCount和expectedModCount
        
      }
    }

详细原因如下:

发现这个位置应该是不会报错的,查找前后文,发现最有可能报错的应该是for循环里面,但是咋一看压根没错!通过查找资料发现:当修改的个数跟期望修改的个数不相等时抛出此异常。

private abstract class HashIterator<E> implements Iterator<E> {
  Entry<K, V> next; // next entry to return
  int expectedModCount; // For fast-fail
  int index; // current slot
  Entry<K, V> current; // current entry
  ...
  final Entry<K, V> nextEntry() {
    if (modCount != expectedModCount)
      throw new ConcurrentModificationException(); // 抛出异常
    Entry<K, V> e = current = next;
    if (e == null)
      throw new NoSuchElementException();

    if ((next = e.next) == null) {
      Entry[] t = table;
      while (index < t.length && (next = t[index++]) == null)
        ;
    }
    return e;
  }
  ...
}

于是查看HashMap.remove()方法代码如下:

/**
   * Removes the mapping for the specified key from this map if present.
   *
   * @param  key key whose mapping is to be removed from the map
   * @return the previous value associated with <tt>key</tt>, or
   *		 <tt>null</tt> if there was no mapping for <tt>key</tt>.
   *		 (A <tt>null</tt> return can also indicate that the map
   *		 previously associated <tt>null</tt> with <tt>key</tt>.)
   */
  public V remove(Object key) {
    Entry<K,V> e = removeEntryForKey(key);
    return (e == null ? null : e.value);
  }

  /**
   * Removes and returns the entry associated with the specified key
   * in the HashMap.  Returns null if the HashMap contains no mapping
   * for this key.
   */
  final Entry<K,V> removeEntryForKey(Object key) {
    int hash = (key == null) ? 0 : hash(key.hashCode());
    int i = indexFor(hash, table.length);
    Entry<K,V> prev = table[i];
    Entry<K,V> e = prev;

    while (e != null) {
      Entry<K,V> next = e.next;
      Object k;
      if (e.hash == hash &&
        ((k = e.key) == key || (key != null && key.equals(k)))) {
        modCount++;
        size--;
        if (prev == e)
          table[i] = next;
        else
          prev.next = next;
        e.recordRemoval(this);
        return e;
      }
      prev = e;
      e = next;
    }

    return e;
  }

你会发现,其中有modCount++操作。

modCount表示修改的次数,而并没有改变其exceptedmodCount;

接下来看看iterator.remove()方法:(java.util.Hashtable.Enumerator.remove())

public void remove() {
      if (!iterator)
    throw new UnsupportedOperationException();
      if (lastReturned == null)
    throw new IllegalStateException("Hashtable Enumerator");
      if (modCount != expectedModCount)
    throw new ConcurrentModificationException();

      synchronized(Hashtable.this) {
    Entry[] tab = Hashtable.this.table;
    int index = (lastReturned.hash & 0x7FFFFFFF) % tab.length;

    for (Entry<K,V> e = tab[index], prev = null; e != null;
         prev = e, e = e.next) {
        if (e == lastReturned) {
      modCount++;
      expectedModCount++;
      if (prev == null)
          tab[index] = e.next;
      else
          prev.next = e.next;
      count--;
      lastReturned = null;
      return;
        }
    }
    throw new ConcurrentModificationException();
      }
  }
    }

而此删除元素的方法,将modCount自增的同时将exceptedModCount同样自增。也就不会抛出异常。


转载于:http://www.tuicool.com/articles/MbyMra

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值