看下面这个例子:
@Test
public void listRemoveTest() {
String[] temp={"Jim","Jim","Amli","Amli","Masu","Lina"};
List<String> names = new ArrayList<String>();
for (int i = 0; i < temp.length; i++) {
names.add(temp[i]);
}
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if("Jim".equals(name) || "Amli".equals(name)) {
names.remove(i);
}
}
for (String name : names) {
System.out.println(name);
}
}
我们的期望值是移除所有名为“Jim”或者“Amli”的对象:
Masu
Lina
而事实上输出结果为:
Jim
Amli
Masu
Lina
造成该结果是由于 移除列表中对象后 列表长多缩短而列表索引值 i 没有做相应的调整,可以将代码修改如下:
for (int i = 0; i < names.size();) {
String name = names.get(i);
if("Jim".equals(name) || "Amli".equals(name)) {
names.remove(i);
continue;
} else {
i++;
}
}
还可以用集合的迭代器来处理,代码如下:
//错误处理方式
for (String name : names) {
if("Jim".equals(name) || "Amli".equals(name)) {
names.remove(name);
}
}
//正确处理方式
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
String name = iterator.next();
if("Jim".equals(name) || "Amli".equals(name)) {
iterator.remove();
}
}
上述的错误方式会造成java.util.ConcurrentModificationException。
同样的问题也存在于Map中。例如下面的代码:
@Test
public void removeTest() {
Map<String, String> params = new HashMap<String, String>();
params.put("STUDENT_A", "S.A");
params.put("STUDENT_B", "S.B");
params.put("TEACHER_A", "T.A");
params.put("TEACHER_B", "T.B");
params.put("TEST", "TEST");
try {
removeTeacherParameters(params);
} catch (Exception e) {
e.printStackTrace();
}
}
//错误的使用方式
private void removeTeacherParameters(Map<String, String> params) {
for(String key:params.keySet()){
if(key.equals("TEST")){
params.remove(key);
}
if(key.indexOf("TEACHER_")==-1){
params.remove(key);
}
}
}
这里会出现java.util.ConcurrentModificationException:
java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java)
at java.util.HashMap$KeyIterator.next(HashMap.java)
出现这个问题原因是由于 集合中的修改次数标量 和 集合的迭代器的期望修改次数标量 没有匹配上造成的具体可以看HashMap中的代码:
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
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++; //只增加了modCount
size--;
if (prev == e)
table[i] = next;
else
prev.next = next;
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
而AbstractHashedMap中的代码显示在进行next()操作的时候会检测modCount 是否和 expectedModCount相等:
public class AbstractHashedMap extends AbstractMap implements IterableMap {
protected transient int modCount;
protected static abstract class HashIterator implements Iterator {
protected int expectedModCount;
public boolean hasNext() {
return (next != null);
}
protected HashEntry nextEntry() {
//当expectedModCount和modCount不相等时,就抛出ConcurrentModificationException<br>
if (parent.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
HashEntry newCurrent = next;
if (newCurrent == null) {
throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY);
}
HashEntry[] data = parent.data;
int i = hashIndex;
HashEntry n = newCurrent.next;
while (n == null && i > 0) {
n = data[--i];
}
next = n;
hashIndex = i;
last = newCurrent;
return newCurrent;
}
}
protected static class HashMapIterator extends HashIterator implements MapIterator {
public Object next() {
return super.nextEntry().getKey();
}
}
}
而正确的使用方法如下:
@Test
public void removeTest() {
Map<String, String> params = new HashMap<String, String>();
params.put("STUDENT_A", "S.A");
params.put("STUDENT_B", "S.B");
params.put("TEACHER_A", "T.A");
params.put("TEACHER_B", "T.B");
params.put("TEST", "TEST");
try {
removeStudentParameters(params);
} catch (Exception e) {
e.printStackTrace();
}
}
//正确的使用方式
private void removeStudentParameters(Map<String, String> params) {
Iterator<Entry<String, String>> it = params.entrySet().iterator();
while (it.hasNext()) {
String key = it.next().getKey();
if(key.equals("TEST") || key.indexOf("STUDENT_")==-1){
it.remove();
}
}
}
这是由于在迭代器在进行remove操作时候, 同步了modCount 和 expectedModCount的值:
public class AbstractHashedMap extends AbstractMap implements IterableMap {
protected transient int modCount;
protected static abstract class HashIterator implements Iterator {
protected int expectedModCount;
public void remove() {
if (last == null) {
throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID);
}
if (parent.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
parent.remove(last.getKey());
last = null;
//重新设置了expectedModCount的值,避免了ConcurrentModificationException的产生
expectedModCount = parent.modCount;
}
}
}
产生ConcurrentModificationException的原因就是:
执行remove(Object o)方法之后,modCount和expectedModCount不相等了。然后当代码执行到next()方法时,判断了checkForComodification(),发现两个数值不等,就抛出了该Exception。要避免这个Exception,就应该使用结合内置迭代器的remove()方法。同理,在进行集合迭代的时候往集合中插入新的元素也会造成同样的问题,要解决这个问题是新建一个集合来处理。
参考:
http://stackoverflow.com/questions/602636/concurrentmodificationexception-and-a-hashmap
http://www.blogjava.net/evanliu/archive/2008/08/31/224453.html