ArrayList、LinkedList、HashMap中都有一个字段叫modCount。这个字段的用途,在ArrayList的父类AbstractList源码中有注释,说的很清楚:

/**
* The number of times this list has been <i>structurally modified</i>.Structural modifications are those that change the size of the list,
* or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.
*
* <p>This field is used by the iterator and list iterator implementation returned by the {@code iterator} and {@code listIterator} methods.
* If the value of this field changes unexpectedly, the iterator (or list iterator) will throw a {@code ConcurrentModificationException} in
* response to the {@code next}, {@code remove}, {@code previous}, {@code set} or {@code add} operations. This provides
* <i>fail-fast</i> behavior, rather than non-deterministic behavior in the face of concurrent modification during iteration.
*
* <p><b>Use of this field by subclasses is optional.</b> If a subclass wishes to provide fail-fast iterators (and list iterators), then it
* merely has to increment this field in its {@code add(int, E)} and {@code remove(int)} methods (and any other methods that it overrides
* that result in structural modifications to the list). A single call to {@code add(int, E)} or {@code remove(int)} must add no more than
* one to this field, or the iterators (and list iterators) will throw bogus {@code ConcurrentModificationExceptions}. If an implementation
* does not wish to provide fail-fast iterators, this field may be ignored.
*/

  protected transient int modCount = 0;

为了显示美观,对jdk原文注释进行了换行操作。
原文大意如下:
该字段表示list结构上被修改的次数。结构上的修改指的是那些改变了list的长度大小或者使得遍历过程中产生不正确的结果的其它方式。
该字段被Iterator以及ListIterator的实现类所使用,如果该值被意外更改,Iterator或者ListIterator 将抛出ConcurrentModificationException异常,
这是jdk在面对迭代遍历的时候为了避免不确定性而采取的快速失败原则。
子类对此字段的使用是可选的,如果子类希望支持快速失败,只需要覆盖该字段相关的所有方法即可。单线程调用不能添加删除terator正在遍历的对象,
否则将可能抛出ConcurrentModificationException异常,如果子类不希望支持快速失败,该字段可以直接忽略。
----------------------------------------
例子代码:
单线程操作,添加或者删除元素。

public void deleteTest(){
List<String> list = new ArrayList();
list.add("aaaaaa");
list.add("bbbbbb");
list.add("cccccc");
list.add("dddddd");
list.add("eeeeee"); Iterator it = list.iterator();
int i = 0;
String s = null;
while(it.hasNext()){
if(i==2){
it.remove();// 如果用list.remove(it.next());会报异常
}
System.out.println("第"+i+"个元素"+it.next());
i++ ;
}
System.out.println("----------------");
Iterator it2 = list.iterator();
while(it2.hasNext()){
System.out.println(it2.next());
}
}

  注意:第14行,如果用list.remove(it.next());会报ConcurrentModificationException异常,原因参上。

另:注意it.remove()删除的是最近的一次it.next()获取的元素,而不是当前iterator中游标指向的元素!!
因此,本例中i==2时,删除的其实是bbbbbb,而不是cccccc,这很容易被忽视或者误解。如果想删掉cccccc,正确操作是先调用it.next()获取到具体元素,再判断;而且由于调用了it.next(),此时游标已经指向我们期望删除的值了。想直接数数字进行删除,在这里会容易出错误。

其实我们可以查看Iterator的源码来验证:

public Iterator<E> iterator() {
return new Itr();
}

  返回的是一个Itr对象,这个Itr是ArrayList的内部类

private class Itr implements Iterator<E> {
int cursor; // index of next element to return //下一个元素的游标
int lastRet = -1; // index of last element returned; -1 if no such //上一个元素的
int expectedModCount = modCount; //修改计数器期望值 public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor; //此时的游标,指向的是本次要遍历的对象,因为上一次已经++了,初始值为0,没有++的情况下是第一个元素
if (i >= size) //越界了
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1; //游标指向了下一个元素, 但 i 的值没有变
return (E) elementData[lastRet = i]; //将 i 赋值给lastRet,取的值是方法开始时int i=cursor;中的cursor指向的值,而且最终这个游标的数值赋值给了lastRet
}
public void remove() {
if (lastRet < 0) // 如果没有next()操作就直接remove的话,lastRet=-1,会抛异常
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet); // remove之前,cursor、lastRet的值没有修改,都是上次next之后的值,因此此处的lastRet指向上次next获取的元素
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount; // 手动将ArrayList.remove()后modCount的值赋给expectedModCount,避免引起不一致
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
以上代码告诉我们,iterator.remove()实际是remove了上次next返回的元素,并且为了防止ConcurrentModificationException异常,手动修复了修改计数的期望值,而且如果没有经过next操作就直接remove的话,会因为初始的lastRet=-1而抛出IllegalStateException异常。

  

最新文章

  1. 《HelloGitHub月刊》第09期
  2. Phpstorm 设置取消自动保存
  3. HDU 1520 树形dp裸题
  4. Tomcat启动时报错,Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext
  5. Moebius实现Sqlserver集群~介绍篇
  6. HTML5教程:课时一HTML简介
  7. matlab 对图像操作的函数概览
  8. DBA 经典面试题(1)
  9. Redis 学习数据类型
  10. php回滚
  11. 一些C++的语法
  12. [SHOI2015]脑洞治疗仪
  13. mysql 数据库(二)数据库的基本操作
  14. Eclipse markers窗口使用
  15. ASP.NET 后台页面无法识别服务器控件ID
  16. SHOW_PAGE_TRACE
  17. Apache+jboss群集优化
  18. Oracle错误IMP-00010: 不是有效的导出文件, 头部验证失败 分类: Oracle 2015-07-09 13:56 20人阅读 评论(0) 收藏
  19. 2:1 Strus2架构
  20. spring boot和mybatis集成分页插件

热门文章

  1. C++11新标准:nullptr关键字
  2. Java50道经典习题-程序48 数字加密
  3. SQL Server 2014 清理日志
  4. iOS Programming GitHub
  5. Websocket,ProtoBuffer,Hightlight,JSON 等,最近遇到的一些知识点小结
  6. kuangbin专题16B(kmp模板)
  7. Cardinality (基数)
  8. P3348 [ZJOI2016]大森林
  9. P1402 酒店之王 最大流
  10. EF core 学习 执行原生sql语句 之ExecuteReader 和ExecuteScalar