• Java中的for each实现原理与坑


    文章转载自「开发者圆桌」一个关于开发者入门、进阶、踩坑的微信公众号

    Java中,遍历集合和数组一般有以下三种形式:

    for (int i = 0; i < list.size(); i++) {

        System.out.print(list.get(i) + ",");

    }

    Iterator iter= list.iterator();

    while (iter.hasNext()) {

        System.out.print(iter.next() + ",");

    }

    for (Integer i : list) {

        System.out.print(i + ",");

    }

    第一种是普通的for循环遍历,第二种是使用迭代器进行遍历,第三种我们一般称之为增强for循环也就是for each循环。

    for each实现原理剖析

    我们对以下代码进行反编译:

    for(String str:list){

      System.out.print(str+",");

    }

    反编译后的代码是这样的:

    String str;

    for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.print((new StringBuilder(String.valueOf(str))).append(",").toString())){

       str = (String)iterator.next();

    }

    反编译后的代码虽然有点复杂,不过通过反编译,我们看到Java中的for each循环底层是通过迭代器模式来实现的。

    坑,小心哦

    由于for each循环通过迭代器实现,那么必然有迭代器的特性。在Java中使用迭代器遍历元素的时候,在对集合进行删除的时候一定要注意,使用不当有可能发生ConcurrentModificationException,如以下代码:

    for(String str:list){

    System.out.print(str+",");

    list.remove(str);

    }

    会抛出ConcurrentModificationException异常。

    Iterator被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,当索引指针往后移动的时候就找不到要迭代的对象,所以会抛出ConcurrentModificationException异常。

    Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象,Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

    正确的在遍历的同时删除元素的姿势:

    Iterator<String> Iter = list.iterator();    

    while (Iter.hasNext()) { 

    String str=Iter.next();

    System.out.print(str+",");

            //注意不是list而是Iter的remove。

    Iter.remove();    

    }

    这个细微的问题,可能在大部分情况下由于条件的限制不会出现,一旦条件满足,问题便暴露出来,这就是为什么明明运行良好的代码突然报错了。因此在使用for each循环时一定要注意这个坑。


    延伸阅读

    我们在最初学习Java的时候,会接触到两个命令:javac和java,那个时候我们就知道,javac是用来编译Java类的,就是将我们写好的helloworld.java文件编译成helloworld.class文件。

    那么反编译呢,就是通过helloworld.class文件得到java文件(或者说是程序员能看懂的Java文件)。

    常用的反编译工具有jad、jd-gui等,本文使用的是jad,可以去https://varaneckas.com/jad/下载该工具。

  • 相关阅读:
    同一个ip、不同端口的两个应用,cookie竟然是可以互相访问到
    JS类型判断typeof、instanceof、Object.prototype.toString.call()
    js 控制一次加载一张图片,加载完成后再加载下一张
    cookie机制和session机制的区别
    Do not access Object.prototype method 'hasOwnProperty' from target object
    Vue 中使用mockjs模拟后端数据
    vue遍历数据字典
    this的用法
    关于html5的离线缓存(转帖)
    js正则 (二)
  • 原文地址:https://www.cnblogs.com/helloworld114/p/6511070.html
Copyright © 2020-2023  润新知