foreach循环和for循环

foreach循环和for循环

  foreach即增强for循环,相比for来讲最直观的gua

反编译对比

  测试代码

for源码

1
2
3
4
5
6
7
8
9
10
11
12
public class For {

public static void main(String[] args) {
List<String> strings = new ArrayList<>();
strings.add("Brown");

for (int index = 0;index<strings.size();index++){
System.out.println(strings.get(index));
}
}

}

  idea反编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class For {
public For() {
}

public static void main(String[] args) {
List<String> strings = new ArrayList();
strings.add("Brown");

for(int index = 0; index < strings.size(); ++index) {
System.out.println((String)strings.get(index));
}

}
}

  javap反编译字节码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=3, args_size=1
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String Brown
11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
16: pop
17: iconst_0
18: istore_2
19: iload_2
20: aload_1
21: invokeinterface #6, 1 // InterfaceMethod java/util/List.size:()I
26: if_icmpge 51
29: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
32: aload_1
33: iload_2
34: invokeinterface #8, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
39: checkcast #9 // class java/lang/String
42: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
45: iinc 2, 1
48: goto 19
51: return
}

  逐字逐句翻译为java 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<String> strings = new ArrayList<>();//0-4
String str = "Brown";//8-9
strings.add(str);//7,11-16
int i = 0; //17-18
if(i>= strings.size()){//19-26
goto return // 跳到方法结束
}

PrintStream pw = System.out;//29-32
Object obj = strings.get(i);//33-34
String str = (String)obj;//39 checkcast 由强转产生
pw.println(str);//42
i++;//45
goto if(i>= strings.size())//48 跳转到 i和size 判断的地方
return // 可以理解为结尾的一个大括号,跳出方法体

  由于压根没有goto的语法,所以得翻译成符合java语言规范的代码:

1
2
3
4
5
6
7
List<String> strings = new ArrayList<>();
strings.add("Brown");
int i = 0;
for(;i<string.size();){
System.out.println((String)strings.get(i));
i++;
}

  这里可以发现,每次调用 i< strings.size() 的时候. List的size 方法都会被调用一次

foreach源码

1
2
3
4
5
6
7
8
9
10
public class Foreach {
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
strings.add("Brown");

for (String name:strings){
System.out.println(name);
}
}
}

  此时,控制台输出: Brown

  idea反编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Foreach {
public Foreach() {
}

public static void main(String[] args) {
List<String> strings = new ArrayList();
strings.add("Brown");
Iterator var2 = strings.iterator();

while(var2.hasNext()) {
String name = (String)var2.next();
System.out.println(name);
}

}
}

  非常智能的分析出来了,foreach 编译为字节码之后,实际就是调用Iterator进行遍历.

  javap反编译

javap -v com/aya/Foreach.class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String Brown
11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
16: pop
17: aload_1
18: invokeinterface #6, 1 // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
23: astore_2
24: aload_2
25: invokeinterface #7, 1 // InterfaceMethod java/util/Iterator.hasNext:()Z
30: ifeq 53
33: aload_2
34: invokeinterface #8, 1 // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
39: checkcast #9 // class java/lang/String
42: astore_3
43: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
46: aload_3
47: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
50: goto 24
53: return
}

  从字节码的层面来看,foreach 就是一个while

  逐字逐句翻译伪代码如下:

1
2
3
4
5
6
7
8
9
10
List<String> strings = new ArrayList<>();//0-4, 通过LocalVariableTable 拿到类型和变量名,类型通过LocalVariableTypeTable 获得
strings.add("Brown");//8-16
Iterator iterator = strings.iterator();//17-18
Boolean has = iterator.hasNext() //23-25
if(has){//30 ifeq 栈顶为0 就跳到结束
String next = (String)iterator.next(); // 33-39 , checkcase在强转时产生这个字节码
System.out.println(next);//42-47
goto has = iterator.hasNext() //50 由于java 没有goto 语法,所以if判断应该改为while
}
return;

  整理伪代码逻辑:

1
2
3
4
5
6
List<String> strings = new ArrayList<>();
strings.add("Brown");
Iterator iterator = strings.iterator();
while(iterator.hasNext()){
System.out.println((String)iterator.next());
}

  idea 分析还是更加智能一些,在不是必要的情况下,用idea 反编译就可以.

  这里可以发现,每次遍历的时候,都是调用Iterator 的hasNext和next 方法, iterator只调用了一次

二者区别

for循环

  结构

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

}

  每层遍历判断一次,所以可以在循环中新增或删除元素,但++操作会导致可能跳过某个元素,使用时需要注意逻辑。

foreach循环

  结构

1
2
3
for(a : list){

}

  foreach可以遍历所有实现了Iterable接口的对象数组,循环中通过游标判断是否还有下一个元素,但区别于for循环其只会进行一次边界值计算,若在循环中新增或删除元素会抛出异常java.util.ConcurrentModificationException,只有通过迭代器的remove或add方法才可在循环中改变原始数目

编程测试

  在foreach循环中删除元素

1
2
3
4
5
6
7
8
9
10
List<String> list1 = new ArrayList<>();
list1.add("a");
list1.add("b");
list1.add("c");
list1.add("d");

for(String s : list1){
if(s.equals("b"))
list1.remove(s);
}

  抛出异常:

1
2
3
4
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:937)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:891)
at synsugar.foreach.main(foreach.java:35)

  相关源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;

// prevent creating a synthetic constructor
Itr() {}

public boolean hasNext() {
return cursor != size;
}

@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}

public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();

try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}

@Override
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int size = ArrayList.this.size;
int i = cursor;
if (i < size) {
final Object[] es = elementData;
if (i >= es.length)
throw new ConcurrentModificationException();
for (; i < size && modCount == expectedModCount; i++)
action.accept(elementAt(es, i));
// update once at end to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
}

final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}

  两种循环的删除操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for(int i = 0;i < list.size();i++){
if(i == 1)
list.remove(1);
}
System.out.println(list);

List<String> list1 = new ArrayList<>();
list1.add("a");
list1.add("b");
list1.add("c");
list1.add("d");

for(Iterator<String> iterator = list1.iterator();iterator.hasNext();){
String s = iterator.next();
if(s.equals("b"))
iterator.remove();
}
System.out.println(list1);
}

  输出结果:

1
2
[a, c, d]
[a, c, d]

参考博客和文章书籍等:

https://blog.csdn.net/mz4138/article/details/80975010

因博客主未标明不可转载,若内容涉及侵权请及时告知,我会尽快修改和删除相关内容