首页 技术 正文
技术 2022年11月9日
0 收藏 437 点赞 4,422 浏览 2567 个字

阿里java开发手册已经发表,很多都值得认真研究思考,看到零度的思考题,没忍住研究了一下。

零度的思考:https://mp.weixin.qq.com/s/dDR21k30s6ZVfDvl8BVQmA对foreach循环的思考首先,看一下给出的反例的执行结果。1. 如果是”1″,最后list中的元素为[“2”]2. 如果把”1″换成”2″,会抛出ConcurrentModificationException异常为什么会出现这种情况?这就要考察foreach的执行过程了。 

1. 代码编译foreach其实是一种语法糖,通过简单明了的java语法,实现相对复杂的功能,通过查看代码编译之后的字节码文件,可以看到,foreach的循环会在编译期被转为迭代器(Iterator)的方式,以下是反编译之后的代码

List<String> list = Lists.newArrayList();
list.add("1");
list.add("2");
Iterator var2 = list.iterator();
while(var2.hasNext()) {
String num = (String)var2.next();
if("2".equals(num)) {
list.remove(num);
}
}

2. ArrayList中的Iterator考察

在ArrayList内部有一个Itr的内部类,该内部类实现了Iterator接口,通过ArrayList的iterator()函数获取到的就是内部类Itr的实例对象。 内部类Itr的属性说明cursor:下一个返回元素的索引lastRet:上一个返回元素的索引,如果没有,就是默认值-1expectedModCount:默认值是modCount(是AbstractList的属性,表示集合结构发生变化的次数,每次add或remove都会加1),从变量定义上就可以看出它是一个期望值,用于在遍历的过程中查看ArrayList的结构有没有发生变化,有点类似CAS的做法

内部类Itr的主要函数说明

hasNext()函数

用于判断是否遍历到了集合的末尾

return cursor != size;

checkForComodification()函数

在next()和remove()函数中都会首先调用该方法来判断集合的结构是否发生变化,如果结构发生了变化,modCount就会加1,不等于expectedModCount,就会抛出异常

if (modCount != expectedModCount)
throw new ConcurrentModificationException();

 next()函数

1. 先检查集合的结构有没有发生变化,若是,则抛出异常2. 判断cursor有没有超过集合元素的个数3. 判断cursor有没有超过ArrayList底层数组结构的大小,若是,则抛出异常4. cursor加1,lastRet设置为当前返回元素的索引

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];

remove()函数

1. lastRet判断是否小于0,若是,表示还未开始遍历集合,迭代器当前的索引位于集合第一个元素之前2. 判断元素的结构有没有发生变化3. 通过ArrayList的remove函数去除lastRet索引位置的元素,此时modCount加14. cursor回退到lastRet的索引位置,lastRet设为-1,expectedModCount设置为当前modCount值

if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}

3. 结合代码分析反例

条件为”1″.equals(num)的情况第一次循环,使得cursor为1,此时,符合判断条件,调用集合的remove函数删除元素,modCount加1,此时不等于expectedModCount,size减1变为1第二次循环,调用hasNext函数,此时cursor和size都为1,判断集合中没有可遍历的元素,遍历到了末尾,结束循环,集合中为”2″的元素是没有遍历到的最终打印出集合,结果显示为[“2″] 条件为”2”.equals(num)的情况第一次循环,使得cursor为1,此时,不符合判断条件第二次循环,hasNext调用后发现集合中还有元素,继续遍历,cursor为2,此时,符合判断条件,调用集合的remove函数删除元素,modCount加1,此时不等于expectedModCount,size减1变为1第三次循环,hasNext函数调用的时候,cursor为2,大于size,两者也不相等,返回true,继续执行循环体,此时,会调用next函数,由于next函数首先会判断集合的结构有没有发生变化,因为第二次循环中,集合的结构已经变化了,因此会抛出ConcurrentModificationException异常

4. 为什么正例就不会出现这种问题

因为删除元素调用的是迭代器的remove函数,size变化的同时,cursor也发生了变化,不会出现cursor大于size的情况,同时,集合结构发生变化之后,迭代器的remove函数中对expectedModCount重新设值,感知到了结构的变化 最后,并发操作,需要对迭代器加锁,就不在此赘述了。

对foreach循环的思考

对foreach循环的思考对foreach循环的思考

对foreach循环的思考

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,491
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,907
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,740
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,493
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:8,132
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:5,294