Java内存分配

时间:2016-06-05 10:01:29

标签: java recursion stack-overflow

我有一个问题,我一直在思考。比如这个特定的课程

class A{

private static ArrayList<String> listOne;

public Static ArrayList<String> getList()
  {
    return this.listOne
  }
}  

假设我有一个B类,它拥有一个用listOne读取细节的方法。要查看arraylist,我需要首先获取列表的大小,以便我的代码知道arraylist何时结束。有两种方法我可以这样做,一种是

int listSize = A.getList().size()
for(int count =0; count < listSize; count++)
{
  // code to read through arraylist
}

或者我可以用

实现同样的目的
for(int count=0; count < A.getList().size(); count++)
{
  // code to read through arraylist
}

在内存和效率方面,哪种方法更好?此外,我说我正在递归地读取一个非常大的数组。为简单起见,我们假设递归读取此数组会导致堆栈溢出异常。在这种情况下,第一种方法理论上会导致堆栈溢出更早发生,然后第二种方法看到每个递归调用的堆栈帧必须保持变量的状态&#34; listSize&#34;?

3 个答案:

答案 0 :(得分:2)

查看javap -verbose的结果:

 0: invokestatic  #2                  // Method A.getList:()Ljava/util/ArrayList;
 3: invokevirtual #3                  // Method java/util/ArrayList.size:()I
 6: istore_1
 7: iconst_0
 8: istore_2
 9: iload_2
10: iload_1
11: if_icmpge     27
14: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
17: iload_2
18: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
21: iinc          2, 1
24: goto          9
27: iconst_0
28: istore_2
29: iload_2
30: invokestatic  #2                  // Method A.getList:()Ljava/util/ArrayList;
33: invokevirtual #3                  // Method java/util/ArrayList.size:()I
36: if_icmpge     52
39: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
42: iload_2
43: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
46: iinc          2, 1
49: goto          29
52: return

第一种情况是:

 9: iload_2
10: iload_1
11: if_icmpge     27
14: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
17: iload_2
18: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
21: iinc          2, 1
24: goto          9

第二个:

29: iload_2
30: invokestatic  #2                  // Method A.getList:()Ljava/util/ArrayList;
33: invokevirtual #3                  // Method java/util/ArrayList.size:()I
36: if_icmpge     52
39: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
42: iload_2
43: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
46: iinc          2, 1
49: goto          29

如您所见,它将在每次循环迭代期间获取列表及其大小。 但是,这可能是由JIT优化的,因此结果并不仅仅是编译后的字节码。

创建自:

import java.io.*;
import java.util.*;

public class Z {

    public static void main(String[] args) throws Exception {
        int listSize = A.getList().size();
        for(int count =0; count < listSize; count++) {
            System.out.println(count);
        }
        for(int count =0; count < A.getList().size(); count++) {
            System.out.println(count);
        }
    }

}

class A{

    private static ArrayList<String> listOne = new ArrayList<>(Arrays.asList("1", "2", "3"));

    public static ArrayList<String> getList()
    {
        return listOne;
    }
}  

答案 1 :(得分:1)

两个循环都是一样的。第二个是更好的编码,因为它减少了代码行。 既然你提到你需要遍历列表,那么使用增强(for-each)for循环要好得多。

What are the Advantages of Enhanced for loop and Iterator in Java?

why is enhanced for loop efficient than normal for loop

答案 2 :(得分:0)

关于哪种方法更有效,我认为它们不会有任何明显的差异。依赖于JVM,就像Germann所说,编译器甚至会对此进行优化。所以,不要担心这种微不足道的差异。

我个人会使用第二种方法,因为它的代码行数较少而且我很懒...

然而,为什么不使用?

有一个非常酷的选择,它的名字是...... JON CENA 增强的for循环。

让我们将它与正常的循环进行比较:

正常:

for (int i = 0 ; i < A.getList().size() ; i++ {

}

增强型:

for (String item : A.getList()) {
    // Instead of using A.getList().get(i) to access the items, just use "item"!
}

看起来真好!

这两个for循环之间的主要区别是

  • 循环的正常只是一种带有初始化和增量的while循环。
  • 增强型for循环调用.iterator().hasNext().iterator().next()循环。
  • 您需要知道列表的大小才能使用普通的for循环
  • 您的列表只需要实现Iterable,可能Iterator来使用增强的for循环。不需要任何尺寸。

增强的for循环具有以下限制:

  • 您无法同时遍历两个阵列
  • 如果你想知道列表的索引,最好使用普通for循环,因为多次调用indexOf效率不高。