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

在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/下载该工具。

Java中的for each实现原理与坑的更多相关文章

  1. Java中的HashMap的工作原理是什么?

    问答题23 /120 Java中的HashMap的工作原理是什么? 参考答案 Java中的HashMap是以键值对(key-value)的形式存储元素的.HashMap需要一个hash函数,它使用ha ...

  2. 深入理解Java中的HashMap的实现原理

    HashMap继承自抽象类AbstractMap,抽象类AbstractMap实现了Map接口.关系图例如以下所看到的: Java中的Map<key, value>接口同意我们将一个对象作 ...

  3. Java 中的监控与管理原理概述

    点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 程序猿阿朗博客 已经收录,有很多知识点和系列文章. 当前 ...

  4. JAVA中堆栈和内存分配原理

    1.栈.堆 1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量 ...

  5. Java中Atomic包的实现原理及应用

    1. 同步问题的提出 假设我们使用一个双核处理器执行A和B两个线程,核1执行A线程,而核2执行B线程,这两个线程现在都要对名为obj的对象的成员变量i进行加1操作,假设i的初始值为0,理论上两个线程运 ...

  6. Java中线程池的实现原理

    知识点总结 ---------------------------------------------------------------------------------------------- ...

  7. java中异常处理机制的简单原理

    以上是自认为的java异常处理的简单原理,如有不妥之处还请各位大神帮忙指点,谢谢!

  8. Java 中冷门的 synthetic 关键字原理解读

    看JAVA的反射时,看到有个synthetic ,还有一个方法isSynthetic() 很好奇,就了解了一下: 1.定义 Any constructs introduced by a Java co ...

  9. Java中线程池的实现原理-求职必备

    jdk1.5引入Executor线程池框架,通过它把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池,而不用关心该任务是如何执行.被哪个线程执行,以及什么时候执行. 初始化线程池(4种) ...

随机推荐

  1. Strut、Spring、Hibernate如何实现资源整合(SSH)呢?

    Strut.Spring.Hibernate如何实现资源整合(SSH)呢? 其实很简单,我们从Spring与Hibernate的整合中可以看出来,由Spring的配置文件来管理Hiberbate的配置 ...

  2. hive CliDriver 源码分析

    664行 CliDriver main启动 public static void main(String[] args) throws Exception { int ret = new CliDri ...

  3. DBGrid 各属性的设置

    在 Delphi 语言的数据库编程中,DBGrid 是显示数据的主要手段之一.但是 DBGrid 缺省的外观未免显得单调和缺乏创意.其实,我们完全可以在我们的程序中通过编程来达到美化DBGrid 外观 ...

  4. hadoop-1.x的运行实例

    我的环境是hadoop-0.20.2,eclipse:SDK-3.3.2, 源数据为: Apr 23 11:49:54 hostapd: wlan0: STA 14:7d:c5:9e:fb:84 Ap ...

  5. HTML5获取当前的经纬度坐标

    使用IE10可以查看出结果:chrome和ff都没有正确显示: <!DOCTYPE html> <html lang="en"> <head> ...

  6. 一步一步学Java IO

    1.基本概念 1.1.InputStream 最基本的字节输入流,抽象类,定义了读取原始字节的所有基本方法1.1.1.public abstract int read() throws IOExcep ...

  7. CMD修改IP地址

    在操作系统下,我们可以使用"本地连接"的属性来修改IP地址,但是如果我们要在多个IP地址之间切换,使用这种方法未免过于麻烦.我们可以使用NETSH命令来添加,相当简便.使用DOS修 ...

  8. 使用数字签名实现数据库记录防篡改(Java实现)

    本文大纲 一.提出问题 二.数字签名 三.实现步骤 四.参考代码 五.后记 六.参考资料 一.提出问题 最近在做一个项目,需要对一个现成的产品的数据库进行操作,增加额外的功能.为此,需要对该产品对数据 ...

  9. ThreadLocal模式的原理

    在JDK的早期版本中,提供了一种解决多线程并发问题的方案:java.lang.ThreadLocal类.ThreadLocal类在维护变量时,实际使用了当前线程(Thread)中的一个叫做Thread ...

  10. (求租仓库)navigationController .navigationBar 的属性设置

    需要做成的效果如下图的