看下面这个例子:
@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
相关推荐
总的来说,Java集合框架是Java编程中不可或缺的一部分,理解和熟练掌握其原理与使用方法,对于提升代码质量、优化程序性能具有重要意义。在日常开发中,我们需要根据具体需求灵活运用各种集合类和接口,以实现高效的...
* 使用迭代器(Iterator 接口)将集合对象实例转换成 Iterator 对象实例,然后利用 Iterator 中的函数进行输出 * 使用 foreach 语法 迭代器 迭代器其实就是从集合中取元素的方式:每个集合都有取元素的方法,但是...
Java集合框架是Java编程语言中用于存储和管理对象的重要工具,它提供了多种数据结构,如数组、列表、集合并和映射等。集合框架使得程序员能够高效地处理对象,同时提供了丰富的操作接口和实现类。本文档主要针对Java...
这意味着每个集合都可以被迭代,这符合Java集合框架的设计原则。 3. **迭代器的分类** - **正向迭代器**:按照元素在集合中的自然顺序进行遍历,大多数集合类默认提供这种迭代方式。 - **反向迭代器**:从集合的...
本资源摘要信息涵盖了Java集合知识的多个方面,包括集合和数组的区别、Collection接口的方法、ArrayList和LinkedList的特点、队列的特点、泛型的使用等。 1. 集合和数组的区别 集合和数组是Java中两种常用的数据...
本测试主要针对Java集合知识进行考核,包括对象数组、集合与数组的区别、集合操作方法、并发修改异常处理、以及ArrayList、LinkedList和Vector等具体集合类的特点。 1. 对象数组是Java中的一种数据结构,它存储的是...
### Java集合基础习题解析及答案 #### 题目一:统计子字符串在字符串中的出现次数 **题目描述:** 编写一个Java程序,定义一个方法`getCount`,该方法接收两个参数:一个字符串`str`和一个子字符串`s`,返回`s`在`...
迭代器是用于遍历集合中元素的接口,它提供了一种顺序访问集合对象中各个元素的方法,而不必暴露该对象的内部表示。在Java中,迭代器的主要方法包括next()(返回下一个元素)、hasNext()(判断是否有下一个元素)和...
### Java集合类原理详解 #### 1. 集合框架概述 集合框架是Java编程语言的核心组件之一,用于组织和操作数据集。它提供了一种灵活高效的方式来存储和访问对象集合,支持多种数据结构,如列表(List)、集(Set)、映射...
在Java中,`List`接口是`java.util`包的一部分,它定义了用于存储和操作有序元素集合的方法。`List`的一个主要特点是它可以包含重复元素,并且元素按插入顺序排序。Java标准库提供了几种实现`List`接口的类,如`...
迭代器模式(Iterator Pattern)是Java设计模式中的行为模式之一,它提供了一种方法来顺序访问聚合对象的元素,而又不暴露其底层表示。在Java中,迭代器模式被广泛应用于集合类,如ArrayList、LinkedList等,通过...
Java集合框架是Java语言中最重要的组件之一,能够正确使用Java集合框架对于Java程序的开发具有无比的好处。本文将详细解释Java集合框架的实现原理、Collection、List、Set、Map四大类的使用方法和原理。 1. 集合...
Java集合框架(Java Collection Framework)是Java标准库中的一个重要组成部分,它提供了一系列用于存储和操作数据的接口及实现类。通过合理选择不同的集合类型,可以有效地提高程序的运行效率和可维护性。 #### 二...
9. **测试**:为了确保软件质量,开发者可能编写了单元测试或集成测试,用以验证增删改查等核心功能的正确性。 10. **版本控制**:开发过程中,版本控制系统如Git可能被用来管理代码的迭代,便于团队协作和回溯历史...
Java集合框架是Java编程语言中不可或缺的一部分,它提供了一组接口和类,用于高效地存储、管理和操作数据。本篇文章将深入探讨Java集合框架的各个方面,帮助开发者从基础到高级全面掌握这一关键知识。 首先,我们要...
Java集合框架是Java编程语言中用于操作和管理对象集合的核心部分,它提供了一系列的接口和实现类,用于高效地处理各种数据结构。本篇文章将详细探讨Java集合框架的各个方面,包括其核心概念、主要接口、实现原理以及...
Java集合框架是Java编程语言中不可或缺的一部分,它提供了一种高效、灵活的方式来存储和操作数据。这个框架由一系列接口和类组成,它们定义了不同类型的集合,如List、Set和Map,以及它们的操作方法和实现原理。理解...
在Java编程语言中,集合框架是处理对象数组的...总之,理解和熟练掌握Java集合框架中的对象关系处理是成为一名优秀Java开发者的关键技能之一。通过本课程的学习,你可以深化对这些概念的理解,提升你的编程实践能力。