`
smartgear
  • 浏览: 7096 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java for each loop 的实现原理

    博客分类:
  • java
阅读更多

最近在leetCode上做题,参考别人的代码时,看到了如下的code:

public int removeDuplicates(int[] nums) {
    int cur = 0 ; 
    for(int n:nums)
        if(n>nums[cur])
            nums[++cur] = n;
    return cur+1;
}

 

这个函数的功能是删除一个已排序数组中的重复数字,上面这段code比我的实现简洁许多。但是我第一眼看到它时产生了一个疑问。在我的印象中,java 的for each loop中只能做读操作,不能做写操作,否则会抛出ConcurrentModificationException。但是上面的code工作的很好。我记得for each loop 会编译成iterator的操作,但是查了一下Array Class 并没有iterator的功能,array 的for each loop 又是怎么实现的呢?为了搞清楚这个问题做了如下调查。

写了一段在array上for each loop的函数

 

package test;

public class ArrayIterate {	
	public static void main(String[] args){
		int [] a = new int[]{1,2,3,4,5};
		for(int i: a){
			int b = i;
		}
	}
}

 编译后查看class 文件, 如下:

 

// Compiled from ArrayIterate.java (version 1.6 : 50.0, super bit)
public class test.ArrayIterate {
  
  // Method descriptor #6 ()V
  // Stack: 1, Locals: 1
  public ArrayIterate();
    0  aload_0 [this]
    1  invokespecial java.lang.Object() [8]
    4  return
      Line numbers:
        [pc: 0, line: 3]
      Local variable table:
        [pc: 0, pc: 5] local: this index: 0 type: test.ArrayIterate
  
  // Method descriptor #15 ([Ljava/lang/String;)V
  // Stack: 4, Locals: 7
  public static void main(java.lang.String[] args);
     0  iconst_5
     1  newarray int [10]
     3  dup
     4  iconst_0
     5  iconst_1
     6  iastore
     7  dup
     8  iconst_1
     9  iconst_2
    10  iastore
    11  dup
    12  iconst_2
    13  iconst_3
    14  iastore
    15  dup
    16  iconst_3
    17  iconst_4
    18  iastore
    19  dup
    20  iconst_4
    21  iconst_5
    22  iastore
    23  astore_1 [a]
    24  aload_1 [a]
    25  dup
    26  astore 5
    28  arraylength
    29  istore 4
    31  iconst_0
    32  istore_3
    33  goto 47
    36  aload 5
    38  iload_3
    39  iaload
    40  istore_2 [i]
    41  iload_2 [i]
    42  istore 6
    44  iinc 3 1
    47  iload_3
    48  iload 4
    50  if_icmplt 36
    53  return
      Line numbers:
        [pc: 0, line: 5]
        [pc: 24, line: 6]
        [pc: 41, line: 7]
        [pc: 44, line: 6]
        [pc: 53, line: 9]
      Local variable table:
        [pc: 0, pc: 54] local: args index: 0 type: java.lang.String[]
        [pc: 24, pc: 54] local: a index: 1 type: int[]
        [pc: 41, pc: 44] local: i index: 2 type: int
      Stack map table: number of frames 2
        [pc: 36, full, stack: {}, locals: {java.lang.String[], int[], _, int, int, int[]}]
        [pc: 47, same]
}

 嗯...... 没看懂大笑

 

再仔细看,main 函数的

28 arraylength

29 istore 4

这两行是取了数组长度然后存入4号变量 

44  iinc 3 1

47  iload_3

48  iload 4

50  if_icmplt 36

这四行是,

3号变量增大1

load 3号变量到栈顶

load 4号变量到栈顶

比较栈顶的两个操作数如果第一个小于第二个则跳转到36也就是循环起始的位置。

看明白这几行大概就知道是怎么执行的了,array 的for each loop会编译成一个基于array length的loop。

与写一个for(int i=0; i<array.length;i++){}具有一样的效果。

再看看List上的for each loop是怎么回事。

package test;

import java.util.ArrayList;

public class ListIterate {
	public static void main(String[] args){
		ArrayList<Integer> a= new ArrayList<Integer>();
		a.add(1);
		a.add(2);
		a.add(3);
		a.add(4);
		a.add(5);
		for(int i : a){
			int b = i;
		}
	}
}

 编译后的结果

// Compiled from ListIterate.java (version 1.6 : 50.0, super bit)
public class test.ListIterate {
  
  // Method descriptor #6 ()V
  // Stack: 1, Locals: 1
  public ListIterate();
    0  aload_0 [this]
    1  invokespecial java.lang.Object() [8]
    4  return
      Line numbers:
        [pc: 0, line: 5]
      Local variable table:
        [pc: 0, pc: 5] local: this index: 0 type: test.ListIterate
  
  // Method descriptor #15 ([Ljava/lang/String;)V
  // Stack: 2, Locals: 5
  public static void main(java.lang.String[] args);
     0  new java.util.ArrayList [16]
     3  dup
     4  invokespecial java.util.ArrayList() [18]
     7  astore_1 [a]
     8  aload_1 [a]
     9  iconst_1
    10  invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [19]
    13  invokevirtual java.util.ArrayList.add(java.lang.Object) : boolean [25]
    16  pop
    17  aload_1 [a]
    18  iconst_2
    19  invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [19]
    22  invokevirtual java.util.ArrayList.add(java.lang.Object) : boolean [25]
    25  pop
    26  aload_1 [a]
    27  iconst_3
    28  invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [19]
    31  invokevirtual java.util.ArrayList.add(java.lang.Object) : boolean [25]
    34  pop
    35  aload_1 [a]
    36  iconst_4
    37  invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [19]
    40  invokevirtual java.util.ArrayList.add(java.lang.Object) : boolean [25]
    43  pop
    44  aload_1 [a]
    45  iconst_5
    46  invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [19]
    49  invokevirtual java.util.ArrayList.add(java.lang.Object) : boolean [25]
    52  pop
    53  aload_1 [a]
    54  invokevirtual java.util.ArrayList.iterator() : java.util.Iterator [29]
    57  astore_3
    58  goto 77
    61  aload_3
    62  invokeinterface java.util.Iterator.next() : java.lang.Object [33] [nargs: 1]
    67  checkcast java.lang.Integer [20]
    70  invokevirtual java.lang.Integer.intValue() : int [39]
    73  istore_2 [i]
    74  iload_2 [i]
    75  istore 4
    77  aload_3
    78  invokeinterface java.util.Iterator.hasNext() : boolean [43] [nargs: 1]
    83  ifne 61
    86  return
      Line numbers:
        [pc: 0, line: 7]
        [pc: 8, line: 8]
        [pc: 17, line: 9]
        [pc: 26, line: 10]
        [pc: 35, line: 11]
        [pc: 44, line: 12]
        [pc: 53, line: 13]
        [pc: 74, line: 14]
        [pc: 77, line: 13]
        [pc: 86, line: 16]
      Local variable table:
        [pc: 0, pc: 87] local: args index: 0 type: java.lang.String[]
        [pc: 8, pc: 87] local: a index: 1 type: java.util.ArrayList
        [pc: 74, pc: 77] local: i index: 2 type: int
      Local variable type table:
        [pc: 8, pc: 87] local: a index: 1 type: java.util.ArrayList<java.lang.Integer>
      Stack map table: number of frames 2
        [pc: 61, full, stack: {}, locals: {java.lang.String[], java.util.ArrayList, _, java.util.Iterator}]
        [pc: 77, same]
}

看过main的54, 62,78发现,果真是基于Iterator实现的。

首先获取iterator,

然后调用iterator.next() 获取当前element。

接着调用iterator.hasNext()

然后判断刚才获取的值是否为真,是则跳转到61循环开始的位置。

至此基本上搞明白了java for each loop的实现原理。

 

顺便又看了一下ArrayList的javadoc,

The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.

 

发现不是任何写操作都会引起ConcurrentModificationException, 只有structurally modified才会。啥叫structurally modified?这个取决于list的具体实现,对ArrayList来说,iterate一个ArrayList时,对这个list增减element就是structurally modified。如果修改element的property则没有问题。

 

 

 

 

 

分享到:
评论

相关推荐

    Java-forloop:使用for循环显示结果

    让我们深入探讨Java for循环的工作原理、语法以及如何利用它来显示结果。 1. **for循环的语法** Java中的for循环由三个部分组成,每个部分由分号分隔: ``` for (初始化; 条件; 更新) { // 循环体,执行的代码 ...

    JDK1.5中增强for循环

    随着 Java 技术的不断发展,为了提高开发效率、简化代码编写并增强可读性,JDK 1.5 引入了一系列的新特性,其中就包括了增强 for 循环(Enhanced For Loop),也被称作“for-each”循环。这一特性极大地简化了数组和...

    上海交大 第三学期期末 java复习题和模拟题(附答案).docx

    可以使用增强for循环(`for-each` loop)轻松地遍历其中的元素。 ### 基本数据类型读取 - **知识点**:`DataInputStream`和`FileInputStream`可以用来从文件中读取基本数据类型的数据。 - **解释**:`DataInputStream...

    JAVA参考大全.J2SE.5EDITION.part23至32.rar

    4. **增强的for循环(For-Each Loop)**:也称为foreach循环,使得遍历数组和集合更加简单直观,降低了出错的可能性。 5. **注解(Annotations)**:注解是一种元数据,提供了将信息附加到代码中的方式,而不会影响...

    Java语言规范和JVM规范(7、8、9)

    - 允许在for-each循环中对集合进行修改,但可能会导致不可预期的行为。 - 类型推断的改进,如泛型的钻石操作符,简化了匿名内部类的创建。 2. Java SE 8: - 引入了Lambda表达式和函数式接口,增强了对函数式编程...

    JAVA帮助文档jdk1.5

    4. **for-each循环(Enhanced for loop)**:也称为foreach或迭代器循环,是Java 1.5新增的一种简洁的遍历集合元素的方式,降低了代码的复杂度。 5. **注解(Annotations)**:注解提供了一种元数据机制,允许在...

    《剑指offer》Java中的语法糖.pdf

    6. **增强for循环(For-Each Loop)**:增强for循环简化了遍历集合或数组的代码,如`for (Type item : collection) {...}`,无需手动管理迭代器。 7. **Switch支持字符串和枚举**:Java 7开始,switch语句不仅可以...

    JDK源码(sun包)

    JDK 1.5,也被称为Java SE 5.0,引入了许多重要的语言特性,如泛型(Generics)、枚举(Enums)、自动装箱拆箱、可变参数(Varargs)、注解(Annotations)以及增强的for循环(For-Each Loop)。这些特性极大地提高...

    jdk开发手册 1.6版本和1.5版本 中文

    它引入了诸多新特性,如泛型(Generics)、枚举(Enums)、自动装箱拆箱(Autoboxing/Unboxing)、变长参数(Varargs)以及增强的for循环(For-Each Loop)。这些特性提升了代码的可读性和安全性,减少了类型转换...

    JDK转换器(1.5-->1.4)

    4. **for-each循环(Enhanced for loop)**:也称为增强的迭代器,简化了遍历集合的操作。1.4版本需要使用传统的迭代器来实现相同功能。 5. **可变参数(Varargs)**:允许在方法签名中使用省略号(...)表示可变...

    编译原理龙书答案

    编译原理龙书答案 完整性高 第二章 2.2 Exercises for Section 2.2 2.2.1 Consider the context-free grammar: S -&gt; S S + | S S * | a Show how the string aa+a* can be generated by this grammar. Construct a ...

    java1.5源码-Druga-proba-JMBG-programa:姓氏:LazarRistic。为什么要使用这项技术:在“编程原理”期间

    3. **For-each Loop(foreach循环)**:也称为增强for循环,这是对传统for循环的一个简洁且易于理解的替代。它可以遍历数组和集合,减少了代码量,降低了出错的可能性。 4. **Static Import(静态导入)**:通过...

    MD5加密算法(Java语言描述)

    MD5加密算法(Java版) 可以运行 原理  对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位...

Global site tag (gtag.js) - Google Analytics