前不久去面试了一家公司,面试题是这样的:
一个二维数组赋值,有两种循环方法,问是第一种循环效率高,还是第二种循环效率高,代码如下:
int a[][] = new int[M][N];
int b[][] = new int [M][N];
int c[][] = new int[M][N];
for(int i=0;i<M;i++)
{
for(int j=0;j<N;j++)
{
c[i][j] = a[i][j]*b[i][j];
}
}
for(int j=0;j<N;j++)
{
for(int i=0;i<M;i++)
{
c[i][j] = a[i][j]*b[i][j];
}
}
当时认为一般编程都是用第一种方法遍历,没有推荐用第二种方法的,应该第一种效率要高。
回来测试了一下,当M=1000,N=2000时,第一种循环用时15秒,第二种循环用时63秒,明显第一种效率要高,可是究竟是什么原因呢。
用javap分析第一种for循环
public static void main(java.lang.String[]);
Code:
Stack=5, Locals=6, Args_size=1
0: sipush 1000
3: sipush 2000
6: multianewarray #16, 2; //class "[[I"
10: astore_1
11: sipush 1000
14: sipush 2000
17: multianewarray #16, 2; //class "[[I"
21: astore_2
22: sipush 1000
25: sipush 2000
28: multianewarray #16, 2; //class "[[I"
32: astore_3
33: iconst_0
34: istore 4
36: goto 81
39: iconst_0
40: istore 5
42: goto 70
45: aload_3 //将c的地址压栈
46: iload 4 //将i的值压栈
48: aaload //将上两个值出栈,并将c[i]的值压栈
49: iload 5 //将j的值压栈
51: aload_1
52: iload 4
54: aaload
55: iload 5
57: iaload
58: aload_2
59: iload 4
61: aaload
62: iload 5
64: iaload
65: imul
66: iastore
67: iinc 5, 1
70: iload 5
72: sipush 2000
75: if_icmplt 45
78: iinc 4, 1
81: iload 4
83: sipush 1000
86: if_icmplt 39
89: return
LineNumberTable:
line 18: 0
line 19: 11
line 20: 22
line 23: 33
line 25: 39
line 27: 45
line 25: 67
line 23: 78
line 43: 89
LocalVariableTable:
Start Length Slot Name Signature
0 90 0 args [Ljava/lang/String;
11 79 1 a [[I
22 68 2 b [[I
33 57 3 c [[I
36 53 4 i I
42 36 5 j I
第二种for循环
public static void main(java.lang.String[]);
Code:
Stack=5, Locals=6, Args_size=1
0: sipush 1000
3: sipush 2000
6: multianewarray #16, 2; //class "[[I"
10: astore_1
11: sipush 1000
14: sipush 2000
17: multianewarray #16, 2; //class "[[I"
21: astore_2
22: sipush 1000
25: sipush 2000
28: multianewarray #16, 2; //class "[[I"
32: astore_3
33: iconst_0
34: istore 4
36: goto 81
39: iconst_0
40: istore 5
42: goto 70
45: aload_3
46: iload 5
48: aaload
49: iload 4
51: aload_1
52: iload 5
54: aaload
55: iload 4
57: iaload
58: aload_2
59: iload 5
61: aaload
62: iload 4
64: iaload
65: imul
66: iastore
67: iinc 5, 1
70: iload 5
72: sipush 1000
75: if_icmplt 45
78: iinc 4, 1
81: iload 4
83: sipush 2000
86: if_icmplt 39
89: return
LineNumberTable:
line 18: 0
line 19: 11
line 20: 22
line 34: 33
line 36: 39
line 38: 45
line 36: 67
line 34: 78
line 43: 89
LocalVariableTable:
Start Length Slot Name Signature
0 90 0 args [Ljava/lang/String;
11 79 1 a [[I
22 68 2 b [[I
33 57 3 c [[I
36 53 4 j I
42 36 5 i I
由此可以看出,每次都是从一维数组开始根据内存地址查找,找到一维数组的地址后再找二维数组的地址,然后将此地址的实际值压栈,类似链表结构。第二种for循环因为首先要将数组a的地址入栈,然后遍历第一维数组,然后用aaload将数组当前下标存放的地址值入栈,由于第一维数组不同,所以需要频繁出栈入栈第一维数组,时间就被浪费在这里。