日期:2014-05-20  浏览次数:20669 次

Java面试题之二维数组性能问题

前不久去面试了一家公司,面试题是这样的:

?

一个二维数组赋值,有两种循环方法,问是第一种循环效率高,还是第二种循环效率高,代码如下:

?

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将数组当前下标存放的地址值入栈,由于第一维数组不同,所以需要频繁出栈入栈第一维数组,时间就被浪费在这里。