哪个是最佳的?

时间:2009-12-11 05:13:55

标签: java performance loops

在循环中声明一个变量是好的,还是在Java中最优化地声明。在声明循环内部时是否还有任何性能成本?

例如

选项1:循环外

List list = new ArrayList();
int value;

//populate list
for(int i = 0 ; i < list.size(); i++) {
  value = list.get(i);
  System.out.println(“value is ”+ value);
}

选项2:循环内部

List list = new ArrayList();

//populate list
for(int i = 0; i < list.size(); i++) {
  int value = list.get(i);
  System.out.println(“value is ”+ value);
}

8 个答案:

答案 0 :(得分:12)

Clean Code中,Robert C. Martin建议Java编码人员尽可能地将变量声明为使用它们的位置。变量的范围不应超过必要的范围。将变量的声明接近其使用位置有助于为读者提供类型和初始化信息。不要过分关注性能,因为JVM非常擅长优化这些东西。而是专注于可读性。

BTW:如果您使用的是Java 5或更高版本,则可以使用以下new-for-Java-5功能显着修改代码示例:

  • foreach construct
  • 泛型
  • 自动装箱

我重构了你的例子以使用上述新功能。

List<Integer> list = new ArrayList<Integer>();

// populate list

for (int value : list) {
    System.out.println("value is " + value);
}

答案 1 :(得分:1)

从性能角度来看,实现哪种方式应该没有区别。

但更重要的是,你不应该浪费你的时间微观优化你的代码......除非你已经分析了整个应用程序并确定这个片段是性能瓶颈。 (如果你已经这样做了,那么你可以很好地看看代码的两个版本之间是否存在任何差异。但如果有的话,我会非常惊讶...)

答案 2 :(得分:1)

虽然您的示例是一个假设的,很可能不是真实世界的应用程序,但简单的答案是在这种情况下您根本不需要变量。没有理由分配内存。简单地说,它浪费了内存,成为JVM中的炮灰。您已经分配了内存来将值存储在List中,为什么要在另一个变量中复制它?

变量的使用范围通常决定了它应该被声明的位置。例如:

Connection conn = null;
try {
    conn.open();
} catch (Exception e) {
    e.printStackTrace();
} finally {
    conn.close();
}

其他答案很好地揭露了您提供的示例中的其他陷阱。有更好的方法来遍历列表,无论是使用实际的迭代器还是foreach循环,泛型将有助于消除创建原始副本的需要。

答案 3 :(得分:0)

好吧,如果你担心优化代码 - 我不确定Java如何评估循环,但是在循环声明中调用list.size()可能比循环(和设置)更低效它可能是一个可变的listLength。我很确定这种方法在JavaScript中更快。它可能更高效的原因是在for循环中调用size函数意味着每次运行测试时都必须调用该函数来查看循环是否已完成,而不是针对静态值进行测试。 / p>

答案 4 :(得分:0)

遍历列表的最佳方法是使用 Iterator

for (Iterator iter=list.iterator(); iter.hasNext();) {
  System.out.println(iter.next());
}

答案 5 :(得分:0)

在这种简单的情况下,很可能没有区别,编译器会生成完全相同的代码(假设在声明变量时没有设置初始值)。

在更复杂的情况和更长的函数中,在循环或其他块中声明局部变量可能更高效。这缩短了变量的生命周期,从而使编译器更容易优化代码。当块外部不存在变量时,用于存储变量的寄存器可用于其他目的。

这当然取决于编译器的实现。我不了解Java,但至少有一些C编译器制造商在他们的文档中给出了这个建议。

至于可读性,我的观点是,在简短的函数中,最好在开头可以很容易地找到所有变量。在非常长的函数中(无论如何都应该避免),在块中声明变量可能更好(这恰好也更有效)。

答案 6 :(得分:0)

除了其他人提供的有用建议外,请记住一件事:

从不早期优化。如果您真的认为您的代码很慢并且可能需要改进,那么请使用分析器为您的代码找出瓶颈所在,然后才重构它们。了解错误的位置。下次不要重复错误。

在您的情况下,我会说,根据Java VM版本,您的性能(猜测是什么)可能会有所不同。根据经验,我不会在循环中声明变量;一个int肯定会被编译器优化掉并重新使用相同的内存地址,额外的计算成本可以忽略不计。

但是

如果你在一个循环中声明一个对象,那么情况会有所不同。如果您的对象在创建时执行I / O写入会怎么样?网络DNS查找?你可能不知道/关心。因此,最佳做法是宣布它为止。

另外,不要将性能与最佳实践混淆。他们可能会把你带入危险的领域。

答案 7 :(得分:0)

Java编译器根据您使用局部变量的方式确定堆栈帧中需要多少“槽”。此数字是根据方法中任何给定点的最大活动本地数确定的。

如果您的代码仅为

int value;
for (...) {...}

for (...) {
   int value;
   ...
}

运行时堆栈上只需要一个插槽;无论循环运行多少次,都可以在循环内重用相同的槽。

但是,如果你在循环之后做了一些需要另一个插槽的东西:

int value;
for (...) {...}
int anotherValue;

for (...) {
   int value;
   ...
}
int anotherValue;

我们会看到一个区别 - 第一个版本需要两个插槽,因为两个变量在循环后都是活动的;在第二个例子中,只需要一个插槽,因为我们可以在循环后重用“value”中的“anotherValue”插槽。

注意:根据数据的实际使用方式,编译器可以更聪明地进行优化,但是这个简单的示例旨在证明堆栈帧分配可能存在差异。