foreach 循环的底层原理及正确使用方式,一定要掌握这些!

点击关注公众号,实用技术文章及时了解foreach 循环的底层原理及正确使用方式,一定要掌握这些!


前言

并发修改异常是指:ConcurrentModificationException

foreach循环其实是一种加强的for循环,相较于for循环更加简洁,可以用来遍历数组或者集合,它的底层原理其实是实现了迭代器的功能,所以本质上来说foreach可以遍历任何实现了Iterable接口的对象。

一、foreach底层实现原理

foreach本质上不过是编译器提供的“语法糖”包装。编译器在遇到for(Type item : arrayOrList) { }代码时,会进行代码的转译。

1、当使用foreach对数组进行遍历时,会进行如下转译:

源代码:

foreach 循环的底层原理及正确使用方式,一定要掌握这些!

转译后代码:

foreach 循环的底层原理及正确使用方式,一定要掌握这些!

可以发现转译后的代码其实就是我们平常使用for循环遍历数组的方式,即:

for(int i=0;i<array.length;i++) {
    Type item = array[i];
    System.out.println(item);
}

2、如果是遍历集合类型,则要求被遍历的集合类型必须实现java.lang.Iterable接口,在iterator()方法中返回一个Iterator迭代器。

源代码:

foreach 循环的底层原理及正确使用方式,一定要掌握这些!

转译后代码:

foreach 循环的底层原理及正确使用方式,一定要掌握这些!

可以发现使用foreach遍历集合时,它会被转译为迭代器的方式去实现集合的遍历。即:

Iterator iterator = strings.iterator();
while(iterator.hasNext()){
 String string = (String)iterator.next();
 System.out.println(string);
}

二、foreach使用限制

  1. foreach在遍历集合的过程中不能修改集合中元素的值。不过,如果遍历的是数组,则不受此限制。
  2. foreach在遍历过程中不能往集合中增加或删除元素,否则会抛出ConcurrentModificationException异常。即使在个别特殊情况下没有抛出这个异常,那也是因为巧合(下文会有说明)。
  3. 遍历过程中,集合或数组中同时只有一个元素可见,即只有“当前遍历到的元素”可见,而前一个或后一个元素是不可见的。
  4. 只能从前往后正向遍历,不能反向遍历。
错误使用实例

关于第2点,尝试在foreach遍历过程中,添加或删除元素:

ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
final String toRemove = "2";
final String toAdd = "1000";
for (String item : list) {
    //item = "100"; //这句执行无效,仅仅改变迭代器中item的指向,并不会真正改变list中的元素
    if (toRemove.equals(item)) {
        list.remove(item); //仅当toRemove为"3"时,没有报异常。这是删除倒数第二个元素情况下的“巧合”。
        //list.add(toAdd); // 报ConcurrentModificationException
    }
}

上述情况抛出并发修改异常的原因分析: 1、ArrayList内部有一个成员变量modCount,用来记录list内部元素改变的次数。扩展:最全的java面试题库

2、通过iter=list.iterator()返回一个新的迭代器对象的时候,iter内部会用expectedModCount成员变量记录下当时的modCount的值。在整个循环的遍历过程中,不管是iter.next()还是iter.remove()方法都会检查原ArrayList的modCount值是否与iter内部记录的expectedModCount值一致,一旦不一致就会抛ConcurrentModificationException。因此,异常是在增减集合元素后,下一轮循环的iter.next()方法中抛出的。

3相关源代码

public boolean hasNext() {
    return cursor != size;
}
public E next() {
 // 此处抛出异常
    checkForComodification();
    // 省略其他代码...
}
final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

4、当toRemove为”3″时,没有报异常,这是为什么呢?

因为,这种情况下,一旦remove之后,原ArrayList的size会减少1,下一轮通过iter.hasNext()(hasNext只是返回迭代器内部的迭代位置cursor是否已达到被迭代容器的size,本身不会抛异常)判断是否还有元素时,发现没有了,直接返回false,进而不会调用到iter.next()方法。当然也就不会有从这个方法中抛出的异常啦。

正确使用

那么问题来了,如果要在遍历集合的过程中需要删除或添加元素该怎么办?

1、普通for循环

for(int i=0;i<list.size();i++) {
    String item = list.get(i);
    if ("3".equals(item)) {
        list.remove(i);//为了效率,这里最好不要用list.remove(item)
    }
}

2、Iterator迭代器方式遍历,通过迭代器的remove方法进行删除。

Iterator<String> iter = list.iterator();
while(iter.hasNext()) {
    String item = iter.next();
    if ("4".equals(item)) {
        iter.remove();
    }
}

总结

在 subList 场景中,高度注意对父集合元素的增加或删除,均会导致子列表的遍历、增加、删除产生 ConcurrentModificationException 异常。——阿里巴巴Java开发规范(嵩山版)

感谢阅读,希望对你有所帮助 :) 

来源:blog.csdn.net/m0_51358164/article/details/126665934

推荐

Java面试题宝典

技术内卷群,一起来学习!!

foreach 循环的底层原理及正确使用方式,一定要掌握这些!

PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。“在看”支持我们吧!

foreach 循环的底层原理及正确使用方式,一定要掌握这些!

原文始发于微信公众号(Java知音):foreach 循环的底层原理及正确使用方式,一定要掌握这些!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/41785.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!