哪里有小叶女贞批发商? 当前位置:首页>哪里有小叶女贞批发商?>正文

哪里有小叶女贞批发商?

发布时间:2019-04-21

原标题:对foreach循环的思考

“谁行动不便了。”乔伊脸红耳赤连忙打断的道,就算过去了几天但是每每想起那一晚的事情她都十分羞燥,而布玛却偏偏可恶的经常拿来说可是让乔伊这个面皮薄的女人怎么受得了“我行动不便。”刘皓立刻开口免得乔伊羞得都快哭了:“今天可是三打三的比赛如无意外的话,而我手中只有你们两个,你们有信心吗?”

湖南可以栽植地柏吗?

微薄上,江之然点赞讽刺二言论的话题,一晚上热度就飙至将近三千万,二和江之然粉丝为维护自己偶像各开的话题累计热度也过了两千万,一夜过五千万热度足见两大歌星的高人气。
宁荣荣的九宝琉璃塔光芒绽放,一瞬间就给马红俊施加了攻击增幅,给小舞施加了敏捷增幅。

“装神弄鬼,就算是比真正的老虎强大十倍百倍又如何,看本小姐不将你劈死。”娜美是在场最清楚这个神秘剑豪事情的人,所以在这个剑豪出现的同时毫不留情的一道神的裁决打过去。

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

零度的思考:https://mp.weixin.qq.com/s/dDR21k30s6ZVfDvl8BVQmA
首先,看一下给出的反例的执行结果。
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:上一个返回元素的索引,如果没有,就是默认值-1
expectedModCount:默认值是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加1
4. 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重新设值,感知到了结构的变化
 
最后,并发操作,需要对迭代器加锁,就不在此赘述了。

编辑:石龙石

发布:2019-04-21 05:10:50

当前文章:http://www.nsmsa.com.cn/content/201810/12/content_88241.html

湖北能种植常春藤吗? 路西法月季是藤月吗? 安徽黄玉兰价格便宜吗? 6cm树状月季价格哪里便宜? 剑麻小苗多少钱一棵? 直径12公分的紫玉兰多少钱一棵? 鸡爪槭分枝点一般多高? 九月份能种植爬墙紫藤吗?

53264 86258 85875 62107 58240 54028 95690 73396 93781 72520 98806 48241 27016 69296 58490 40383 58096 40780 95366 43922

责任编辑:陵华