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异常。

  

集合类中modCount字段的作用的更多相关文章

  1. MongoDB中_class字段的作用

    我们知道,如果你用Java的Sping Data 框架映射Pojo为MongoDB数据时,数据库中会自动给你添加一个_class字段,那这个字段是干嘛用的呢?我们可以不可以不要这个字段呢? 直接上结论 ...

  2. 【memcached】memcached中flags字段的作用

    我们一般只注意到memcached的数据结构是key,value,今天看memcached源代码的时候,盯上了flags,没看明白.后来问了一下同事,说PHP当中使用flags标记,标识memcach ...

  3. maven中pom文件中name字段的作用

  4. ABAP 数据字典中的参考表和参考字段的作用

         ABAP数据字典中的参考表和参考字段的作用 大家最初在SE11中创建表和结构的时候都会遇到一个问题,如果设定了某个字段为QUAN或者CURR类型,也就是数量或金额的时候,总会要求输入一个参考 ...

  5. SAP MM 物料主数据采购视图中的字段'Var. OUn'的作用?

    SAP MM 物料主数据采购视图中的字段'Var. OUn'的作用? 物料主数据采购视图里有一个字段,叫做'Var. OUn'的, 如下图: 这个字段,笔者之前所参与的项目里,从来没有用过.所以,笔者 ...

  6. C#中的字段与属性的区别及属性的作用

    C#中的字段与属性的区别及属性的作用 先上代码 public class Employee { //字段 private string name; //属性 public string Name { ...

  7. SQLSERVER中NULL位图的作用

    SQLSERVER中NULL位图的作用 首先感谢宋沄剑提供的文章和sqlskill网站:www.sqlskills.com,看下面文章之前请先看一下下面两篇文章 SQL Server误区30日谈-Da ...

  8. sql 查询表中所有字段的名称

    最近工作用到SQL语句查询表中所有字段的名称,网上查询,发现不同数据库的查询方法不同,例如: SQL server 查询表的所有字段名称:Select name from syscolumns Whe ...

  9. Java集合类中的哈希总结

    JAVA集合类中的哈希总结 目 录 1.哈希表 2.Hashtable.HashMap.ConcurrentHashMap.LinkedHashMap.TreeMap区别 3.Hashtable.Ha ...

随机推荐

  1. jQuery的选择器+实例

    返回目录 jQuery的冒号选择器 表单  :input :text :password :radio :checkbox :submit :image :reset :button :file :h ...

  2. Transfer data to SQL Server from SPC-Light with Excel macros

    公司的QA检测软件SPC-Light,需要从其中读取一些信息至SQL Server数据库,储存或是做其它分析. 先是在Excel的VBE的工具中,引入一个组件Microsoft ActiveX Dat ...

  3. 转:IDEA 与 eclipse 的部分区别!

    Idea 与 Eclipse 快捷键的区别,上为Eclipse的快捷键,下为Idea的快捷键查找类名CTRL + SHIFT + RCTRL + N 查找JAR包中的类CTRL + SHIFT + T ...

  4. fiddler抓包后Jmeter实现登录接口

    登录接口测试时,先要抓取登录接口,我们使用fiddler来抓包,如下图: 然后再使用Jmeter,填写相对于的参数. 查看结果树: 关键在与抓包,搞清楚抓包的信息,可以用fiddler和火狐和谷歌.我 ...

  5. JAVA环境的JAVA_HOME, PATH 和CLASS_PATH设置

    Windows下JAVA用到的环境变量主要有3个,JAVA_HOME.CLASSPATH.PATH.下面逐个分析. 简单来讲, 1.path是os用 classpath java用 JAVA_HOME ...

  6. luogu P3811线性求逆元

    首先扩O:T了一个点(因为上界松),83分. #include <cstdio> using namespace std; int n, p; void exgcd(int a, int ...

  7. socket 连接关闭的TIME_WAIT状态的理由

    MSL就是maximum segment lifetime(最大分节生命期),这是一个IP数据包能在互联网上生存的最长时间,超过这个时间将在网络中消失 TIME_WAIT两点原因: 1.TCP分节可能 ...

  8. element el-tree循环遍历树形结构,并动态赋值disabled属性

    凌晨3点,功夫不负有心人,已经累趴,效果终于出来: 贴上代码: <style scoped> .form { width: 50%; } </style> <templa ...

  9. Til the Cows Come Home (dijkstra算法)

    Bessie is out in the field and wants to get back to the barn to get as much sleep as possible before ...

  10. Lintcode 摊平嵌套的列表

    /** * // This is the interface that allows for creating nested lists. * // You should not implement ...