ArrayList中modCount的作用
在ArrayList中有个成员变量modCount,继承于AbstractList。
这个成员变量记录着集合的修改次数,也就每次add或者remove它的值都会加1。这到底有什么用呢?
先看下面一段测试代码:
package temp; import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
public class demo {
public static void main(String[] args){
List<String> list = new ArrayList<String>();
//CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
list.add("a");
Iterator iterator = list.iterator();
while(iterator.hasNext()){
String str = (String) iterator.next();
list.remove(str);
}
}
}
在使用迭代器遍历集合的时候同时修改集合元素。因为ArrayList被设计成非同步的,所以理所当然会抛异常。但是该抛什么异常才能说明该问题呢?
首先得了解ArrayList的迭代器
public Iterator<E> iterator() {
return new Itr();
}
在调用list.iterator()的时候返回的是一个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;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
主要关注3个点:
1、expectedModCount的初值为modCount
2、hasNext的判断条件为cursor!=size,就是当前迭代的位置不是数组的最大容量值就返回true
3、next和remove操作之前都会先调用checkForComodification来检查expectedModCount和modCount是否相等
如果没checkForComodification去检查expectedModCount与modCount相等,这个程序肯定会报ArrayIndexOutOfBoundsException
这样的异常显然不是应该出现的(这些运行时错误都是使用者的逻辑错误导致的,我们的JDK那么高端,不会出现使用错误,我们只抛出使用者造成的错误,而这个错误是设计者应该
考虑的),为了避免出现这样的异常,定义了检查。所以抛出ConcurrentModificationException异常更能说明问题。
将测试代码改成如下:
package temp; import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
public class demo {
public static void main(String[] args){
List<String> list = new ArrayList<String>();
//CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
Iterator iterator = list.iterator();
while(iterator.hasNext()){
String str = (String) iterator.next();
if(str.equals("d")){
list.remove(str);
}else{
System.out.println(str);
}
}
}
}
输出却是 a b c。
因为在删除 d 的时候cursor为4,size也变成了4。所以hasNext就返回为true了,循环结束,从而后面的元素也不会输出了。
又想,为什么不把hasNext()的判断改为cursor <=size()呢?但是我们还有可能 add()这样的话就会导致数据混乱,事实上线程安全本身就不允许读的时候被修改。
这种问题在多线程情况下,操作同一集合很容易暴露,就算改成同步的Vector问题还是会存在,需要使用CopyOnWriteArrayList。具体原因下篇博客介绍。
ArrayList中modCount的作用的更多相关文章
- Itreatot接口实现类中modCount的作用
modCount只有在本数据结构对应迭代器中才使用,以HashMap为例: private abstract class HashIterator implements Iterator { Entr ...
- jdk8中Spliterator的作用
文章前半部分转自:https://blog.csdn.net/lh513828570/article/details/56673804 之前的时候看集合部分源码没看完,今天又翻了一下,看到了个东西sp ...
- 从`ArrayList`中了解Java的迭代器
目录 什么是迭代器 迭代器的设计意义 ArrayList对迭代器的实现 增强for循环和迭代器 参考链接 什么是迭代器 Java中的迭代器--Iterator是一个位于java.util包下的接口,这 ...
- ArrayList中的一些小细节@JDK8
ArrayList中的一些小细节@JDK8 protected transient int modCount = 0; 该变量用于记录ArrayList的版本号,不可被序列化,每次对ArrayList ...
- 为什么阿里巴巴要求谨慎使用ArrayList中的subList方法
GitHub 3.7k Star 的Java工程师成神之路 ,不来了解一下吗? GitHub 3.7k Star 的Java工程师成神之路 ,真的不来了解一下吗? GitHub 3.7k Star 的 ...
- 谨慎使用ArrayList中的subList方法
转自:https://www.toutiao.com/a6705958780460335619/?tt_from=weixin&utm_campaign=client_share&wx ...
- 面试官:如何在Integer类型的ArrayList中同时添加String、Character、Boolean等类型的数据? | Java反射高级应用
原文链接:原文来自公众号:C you again,欢迎关注! 1.问题描述 "如何在Integer类型的ArrayList中同时添加String.Character.Boolean等 ...
- web.xml中load-on-startup的作用
如下一段配置,熟悉DWR的再熟悉不过了:<servlet> <servlet-name>dwr-invoker</servlet-name> <ser ...
- C#中构造函数的作用
C#中构造函数的作用 共同点: 都是实例化对象,初始化数据的 默认构造是说所有的类都从祖先object那继承了空参的构造方法,你不写与写空参构造都存在,而有参数的构造一般是自己写的,写就有不写就没有, ...
随机推荐
- MySQL必知必会 读书笔记一:简介
了解数据库 数据库(database) 数据库(database) 保存有组织的数据的容器(通常是一个文 件或一组文件). 数据库软件应称为DBMS(数据库管理系统).数据库是通过DBMS创建和操纵的 ...
- 【Android】导航栏(加图片icon)和不同页面的实现(viewpager+tablayout)
先上图,然后说大致步骤,最后再说细节 图片效果:依序点击导航栏左一.左二.中.右二.右一,最后直接滑动页面(不依靠导航栏切换) 大致步骤如下(文末会有完整代码) [1]创建一个类,我这里取名TabBa ...
- 863. All Nodes Distance K in Binary Tree
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode ...
- 【面试必问】python实例方法、类方法@classmethod、静态方法@staticmethod和属性方法@property区别
[面试必问]python实例方法.类方法@classmethod.静态方法@staticmethod和属性方法@property区别 1.#类方法@classmethod,只能访问类变量,不能访问实例 ...
- 行人属性识别 PETA数据集
参见https://blog.csdn.net/hyk_1996/article/details/80322026(内有数据集下载地址) 其中footwearSneaker属性应该是footwearS ...
- 深入理解@RequestBody注解
我写文章历来追求通俗易懂,今天来深入探讨一下@RequestBody注解.提起这个,所有做过mvc开发的同学应该都不陌生,使用上面肯定也是信手拈来. 所以我这里就简单的提一下这个注解的使用: 1.当客 ...
- 构建工具——maven的补充
1.安装jar到本地仓库 有时候有部分jar由于在maven的中央仓库,只能引用本地的,可以将jar安装到本地仓库进行操作(请先确保mvn命令可以正常运行) mvn install:install-f ...
- 20145226夏艺华《网络对抗》第一次实验拓展:shellcode注入+return-to-libc
20145226夏艺华<网络对抗>第一次实验拓展:shellcode注入+return-to-libc shellcode注入实践 编写shellcode 编写shellcode已经在之前 ...
- BZOJ2600_ricehub_KEY
题目传送门 这道题一开始我还以为是贪心,sort一遍直接取中点然后求最优值. 但写了之后才发现错误,设置的谷仓只要是一段区间的中点即可.这段区间的两端一定是两片谷田. 所以枚举区间的左端点,二分右端点 ...
- python字符串的方法介绍
博文取自鱼C论坛文章: http://bbs.fishc.com/forum.php?mod=viewthread&tid=38992&extra=page%3D1%26filter% ...