java for each loop 的实现原理

本文深入探讨了Java中foreach循环的工作原理,对比了数组和列表在foreach循环中的不同实现方式,并解释了何时会触发ConcurrentModificationException。

最近在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则没有问题。

 

 

 

 

 

<think>好的,我现在要处理用户关于Java增强for循环用法的问题。首先,我需要回顾一下用户的问题,确保我准确理解他们的需求。用户问的是“Java enhanced for loop usage example”,也就是想了解Java中增强for循环的具体使用示例。 接下来,我需要回忆增强for循环的基本语法和用法。增强for循环,也称为for-each循环,是在Java 5中引入的,主要用于简化数组和集合的遍历。它的语法结构是for (元素类型 变量名 : 集合或数组) { ... }。相比传统的for循环,它不需要显式地使用索引或迭代器,代码更简洁,可读性更高。 然后,我应该考虑用户可能的实际需求。他们可能想知道如何在不同的数据结构中使用增强for循环,比如数组、List、Set,甚至是自定义对象。此外,用户可能还想了解增强for循环的局限性,比如不能修改集合中的元素,或者在遍历时不能删除元素等注意事项。 接下来,我需要组织回答的结构。首先给出基本语法,然后分点列出不同数据结构的示例,比如数组、List、Set、Map(不过Map需要用entrySet或keySet来遍历),最后提到注意事项。每个示例部分都需要具体的代码示例,并且用代码块展示,这样用户可以直接看到实际的用法。 还要注意用户提到的系统级指令,比如所有行内数学表达式用$...$,但这里可能用不上。但需要确保代码使用正确的格式,比如用三个反引号包裹代码块,并指定语言为java。另外,要生成相关问题,可能需要包括性能、底层原理、与传统循环的区别等。 然后,检查是否有相关的引用可以支持回答。查看用户提供的引用,引用[1]提到了使用Lambda函数的例子,可能与Java 8的流有关,但增强for循环是更早的特性。引用[2]关于反射,引用[3]关于代码风格,引用[4]关于finally块,引用[5]关于推荐系统,似乎都不直接相关。所以可能不需要引用这些内容,但需要确认用户是否有之前的对话需要参考。根据问题,用户之前的问题可能涉及Java的循环结构,但当前问题单独关于增强for循环,所以可能不需要引用之前的回答,除非有相关内容。 接下来,需要确保回答符合用户的要求:结构清晰,分步骤说明,真实可靠。可能需要提到增强for循环的优缺点,比如简化代码但缺乏索引访问,以及在遍历时修改集合会抛出ConcurrentModificationException异常的情况。 最后,生成相关问题。考虑用户可能的后续问题,比如与传统for循环的区别、性能比较、如何避免ConcurrentModificationException、在自定义对象中的使用等。 总结回答的结构: 1. 基本语法介绍 2. 不同数据结构的示例(数组、List、Set、Map) 3. 自定义对象的遍历 4. 注意事项(无法修改集合、并发修改异常) 5. 相关问题 在编写代码示例时,确保语法正确,比如遍历Map时使用entrySet(),自定义类实现Iterable接口的例子可能需要简化,因为实现迭代器对于新手可能较复杂,但可以给出一个简单的例子。 检查是否覆盖了所有关键点,并且语言简洁明了,符合中文表达习惯。避免使用过于专业的术语,但必要时需准确使用。</think>### Java增强for循环用法详解 增强for循环(Enhanced for-loop)是Java 5引入的特性,用于简化数组和集合的遍历操作。其语法为: ```java for (元素类型 变量名 : 集合或数组) { // 操作代码 } ``` #### 一、基本用法示例 1. **遍历数组** ```java int[] numbers = {1, 2, 3, 4, 5}; for (int num : numbers) { System.out.println(num); } ``` 2. **遍历List集合** ```java List<String> fruits = List.of("Apple", "Banana", "Orange"); for (String fruit : fruits) { System.out.println(fruit); } ``` 3. **遍历Set集合** ```java Set<Integer> scores = Set.of(90, 85, 95); for (int score : scores) { System.out.println(score); } ``` 4. **遍历Map集合** 需通过`entrySet()`或`keySet()`转换: ```java Map<String, Integer> ages = Map.of("Alice", 25, "Bob", 30); for (Map.Entry<String, Integer> entry : ages.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } ``` #### 二、自定义对象遍历 若需遍历自定义类对象,需实现`Iterable`接口: ```java class Student implements Iterable<String> { private List<String> courses = List.of("Math", "Java", "Physics"); @Override public Iterator<String> iterator() { return courses.iterator(); } } // 使用示例 Student student = new Student(); for (String course : student) { System.out.println(course); } ``` #### 三、注意事项 1. **只读遍历** 增强for循环不能直接修改集合结构(如删除元素),否则会抛出`ConcurrentModificationException`[^4] 2. **无索引访问** 无法通过索引直接定位元素,需传统for循环替代 3. **性能特性** 对数组的遍历性能与传统for循环相同,但对`LinkedList`等结构可能略慢
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值