最近在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 for循环的工作原理、语法以及如何利用它来显示结果。 1. **for循环的语法** Java中的for循环由三个部分组成,每个部分由分号分隔: ``` for (初始化; 条件; 更新) { // 循环体,执行的代码 ...
随着 Java 技术的不断发展,为了提高开发效率、简化代码编写并增强可读性,JDK 1.5 引入了一系列的新特性,其中就包括了增强 for 循环(Enhanced For Loop),也被称作“for-each”循环。这一特性极大地简化了数组和...
可以使用增强for循环(`for-each` loop)轻松地遍历其中的元素。 ### 基本数据类型读取 - **知识点**:`DataInputStream`和`FileInputStream`可以用来从文件中读取基本数据类型的数据。 - **解释**:`DataInputStream...
4. **增强的for循环(For-Each Loop)**:也称为foreach循环,使得遍历数组和集合更加简单直观,降低了出错的可能性。 5. **注解(Annotations)**:注解是一种元数据,提供了将信息附加到代码中的方式,而不会影响...
- 允许在for-each循环中对集合进行修改,但可能会导致不可预期的行为。 - 类型推断的改进,如泛型的钻石操作符,简化了匿名内部类的创建。 2. Java SE 8: - 引入了Lambda表达式和函数式接口,增强了对函数式编程...
4. **for-each循环(Enhanced for loop)**:也称为foreach或迭代器循环,是Java 1.5新增的一种简洁的遍历集合元素的方式,降低了代码的复杂度。 5. **注解(Annotations)**:注解提供了一种元数据机制,允许在...
6. **增强for循环(For-Each Loop)**:增强for循环简化了遍历集合或数组的代码,如`for (Type item : collection) {...}`,无需手动管理迭代器。 7. **Switch支持字符串和枚举**:Java 7开始,switch语句不仅可以...
JDK 1.5,也被称为Java SE 5.0,引入了许多重要的语言特性,如泛型(Generics)、枚举(Enums)、自动装箱拆箱、可变参数(Varargs)、注解(Annotations)以及增强的for循环(For-Each Loop)。这些特性极大地提高...
它引入了诸多新特性,如泛型(Generics)、枚举(Enums)、自动装箱拆箱(Autoboxing/Unboxing)、变长参数(Varargs)以及增强的for循环(For-Each Loop)。这些特性提升了代码的可读性和安全性,减少了类型转换...
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 -> S S + | S S * | a Show how the string aa+a* can be generated by this grammar. Construct a ...
3. **For-each Loop(foreach循环)**:也称为增强for循环,这是对传统for循环的一个简洁且易于理解的替代。它可以遍历数组和集合,减少了代码量,降低了出错的可能性。 4. **Static Import(静态导入)**:通过...
MD5加密算法(Java版) 可以运行 原理 对MD5算法简要的叙述可以为:MD5以512位分组来处理输入的信息,且每一分组又被划分为16个32位子分组,经过了一系列的处理后,算法的输出由四个32位分组组成,将这四个32位...