3、流程控制,foreach原码分析

109 阅读3分钟

1、语法

流程控制主要为判断if...esle if...else以及循环语句whiledo...whilefor循环


  • if...else语句主要进行判断。jdk8之后引入 optional类和函数式方法可以对if进行替换
//空值判断,替代if进行空置判断
String s1 = "abc";  
Optional.ofNullable(s1).ifPresent(o -> {  
    System.out.println(o);  
});
/*函数式编程,jdk8提供了四大函数式接口
consumer:消费者接口
supplier:供给者接口
function:函数式接口
predicate:断言式接口
*/
  • 循环控制语句whiledo...whilefor
    • whiledo...while的区别是后者先进行一次循环,然后进行判断
    • for循环和while循环都可进行替换
  • break、continue、 return都是跳出循环的意思。break是跳出整个循环,continue是跳出本次循环, return意味着方法的结束
  • switch...case:可以替代if语句,他只能匹配整数值和字符串以及枚举类型。结束时必须有break或者return

2、foreach和fori的区别

  • 数组
        String[] arrays = {"a","b","c"};  
        for (String array : arrays) {  
//            System.out.println(array);  
        }  
        for (int i = 0; i < arrays.length; i++) {  
//            System.out.println(arrays[i]);  
        }  
    }
 0 iconst_3 //数组长度
 1 anewarray #6 <java/lang/String> //堆内创建数组,地址引用值压入栈顶
 4 dup //复制栈顶值
 5 iconst_0 //常量0入栈
 6 ldc #7 <a> //常量a入栈
 8 aastore //弹出栈顶的值、索引、数组引用进行赋值
 9 dup
10 iconst_1
11 ldc #8 <b>
13 aastore
14 dup
15 iconst_2
16 ldc #9 <c>
18 aastore
19 astore_1
//foreach
20 aload_1 //数组变量进栈
21 astore_2 //赋值给第二个变量
22 aload_2 //第二个局部变量进栈
23 arraylength //获取数组长度
24 istore_3 //赋值给第三个局部变量
25 iconst_0 //常量0进栈
26 istore 4 //赋值给第四个局部变量
28 iload 4 //第四个局部变量进栈
30 iload_3 //第三个局部变量进栈
31 if_icmpge 46 (+15) //判断栈顶两个元素
34 aload_2 //第二个局部变量进栈
35 iload 4 //第四个局部变量进栈
37 aaload //数组对应索引的元素进栈
38 astore 5 //赋值给第五个局部变量
40 iinc 4 by 1 //第四个局部变量自增1
43 goto 28 (-15)
//fori
46 iconst_0
47 istore_2
48 iload_2
49 aload_1
50 arraylength
51 if_icmpge 60 (+9)
54 iinc 2 by 1
57 goto 48 (-9)
60 return

从指令可以看出,当对数组进行foreach和fori的时候,两者的原理实际上是相通的,foreach实际上还是调用的数组长度进行循环然后取值

  • list集合
//arraylist  
ArrayList<String> list = new ArrayList<>();  
list.add("a");  
list.add("b");  
for (String s : list) {  
}  
for (int i = 0; i < list.size(); i++) {  
}  
//LinkedList  
LinkedList<String> linkedList = new LinkedList<>();  
linkedList.add("a");  
linkedList.add("b");  
for (String s : linkedList) {  
}  
for (int i = 0; i < linkedList.size(); i++) {  
}
//看for循环部分的代码
 22 aload_1 //加载局部变量1
 23 invokevirtual #13 <java/util/ArrayList.iterator : ()Ljava/util/Iterator;> //调用iterator
 26 astore_2 //赋值给第二个局部变量
 27 aload_2 //加载局部变量
 28 invokeinterface #14 <java/util/Iterator.hasNext : ()Z> count 1 //调用hasnext
 33 ifeq 49 (+16) //判断
 36 aload_2 //
 37 invokeinterface #15 <java/util/Iterator.next : ()Ljava/lang/Object;> count 1
 42 checkcast #6 <java/lang/String>
 45 astore_3
 46 goto 27 (-19)
 //fori
 49 iconst_0
 50 istore_2
 51 iload_2
 52 aload_1
 53 invokevirtual #16 <java/util/ArrayList.size : ()I>
 56 if_icmpge 65 (+9)
 59 iinc 2 by 1
 62 goto 51 (-11)

 87 aload_2
 88 invokevirtual #20 <java/util/LinkedList.iterator : ()Ljava/util/Iterator;>
 91 astore_3
 92 aload_3
 93 invokeinterface #14 <java/util/Iterator.hasNext : ()Z> count 1
 98 ifeq 115 (+17)
101 aload_3
102 invokeinterface #15 <java/util/Iterator.next : ()Ljava/lang/Object;> count 1
107 checkcast #6 <java/lang/String>
110 astore 4
112 goto 92 (-20)
//fori
115 iconst_0
116 istore_3
117 iload_3
118 aload_2
119 invokevirtual #21 <java/util/LinkedList.size : ()I>
122 if_icmpge 131 (+9)
125 iinc 3 by 1
128 goto 117 (-11)

可以看出集合的遍历,foreach调用的是iterator,而fori是通过索引进行调用的。 需要注意的是fori获取数据,由于arraylist内部维护有索引,所以可以直接取到数据,但是linkedlist不同 我们可以通过get方法查看到

public E get(int index) {  
    checkElementIndex(index);  
    return node(index).item;  
}

Node<E> node(int index) {  
    // assert isElementIndex(index);  
  
    if (index < (size >> 1)) {  
        Node<E> x = first;  
        for (int i = 0; i < index; i++)  
            x = x.next;  
        return x;  
    } else {  
        Node<E> x = last;  
        for (int i = size - 1; i > index; i--)  
            x = x.prev;  
        return x;  
    }  
}
//可以看出linkedlist取数据的时候会进行遍历,所以就相当于foreach多了一层循环