Java中`for`循环的最佳实践

3

可能重复:
for循环优化

在Java中,我有一段代码块:

List e = {element1, element2, ...., elementn};
for(int i = 0; i < e.size(); i++){//Do something in here
};

另一个块:

List e = {element1, element2, ...., elementn};
int listSize = e.size();
for(int i = 0; i < listSize; i++){//Do something in here
};

我认为第二个代码块更好,因为在第一个代码块中,如果 i++,我们需要再次计算 e.size() 来比较 for 循环中的条件。这是对还是错? 那么,比较上述两个代码块,编写 for 循环的最佳实践是什么?为什么?请清晰地解释,并尝试使用此循环。


1
第二个,因为你不需要在每次迭代中计算列表大小。 - Nuno Costa
12个回答

14

我个人会使用增强型for循环语句:

for (Object element : e) {
    // Use element
}

当然,如果你需要索引,那就另当别论。

如果非要使用这两种形式之一,我会选择第一种,因为它更加整洁(它不会引入只在该循环中使用的另一个本地变量),直到我有确凿的证据表明它会导致问题。(在大多数列表实现中,e.size()是一个简单的变量访问,可以被JIT内联。)


1
正如我之前在链接中所读到的,第二个代码块比第一个更好!:( - Kingfisher Phuoc
2
@Kingfisher:这是针对 Android 设备而言的,因为它的 JIT 更加原始,尽管我也不会感到惊讶如果它现在已经能够应对这种情况了。但总的来说,不要没有证据就进行微观优化。 - Jon Skeet

6

通常情况下,最简洁易读的代码是最好的选择,所有条件相等。对于Java而言,增强型for循环(可与实现了Iterable接口的任何类一起使用)是最佳选择。

for (Object object : someCollection) { // do something }

就你发布的两个选项而言,我认为第一个是更好的选择。它更易读,并且你必须记住,在幕后,JIT将尝试优化你编写的大量代码。

编辑:你听说过“过早优化是万恶之源”这句话吗?你的第二个代码块就是过早优化的一个例子。


正如Jon Skeet所建议的那样,如果由于某种原因您需要迭代的集合中的索引,请使用循环的第一种形式。 - Jon
1
+1 可行且全面的 - GingerHead
1
根据我在这里阅读的内容,第二个代码块比第一个更好! - Kingfisher Phuoc
@Kingfisher 这很有趣。但是在文档开头确实写着:“本文档是关于 Android 特定的微优化,因此它假定您已经使用分析工具来确定需要进行优化的代码”。 - Jon

5

如果需要一个索引变量,我会始终使用以下方法:

List e = {element1, element2, ...., elementn};
for(int i = 0, size = e.size(); i < size; i++){
    // Do something in here
};

由于e.size()可能是一个昂贵的操作。

您的第二个选择不好,因为它引入了一个在for循环之外的新变量。我建议尽可能限制变量的可见性。

否则

for (MyClass myObj : list) {
    // Do something here
}

这种方法更加简洁,但可能会稍微影响性能(索引方法不需要实例化 Iterator)。


对于for循环中的大小变量初始化的+1,我认为这非常优雅(尽管我不相信微观优化,除非确实必要)。无论如何,好片段 :) - szegedi
-1 e.size() 不是一个昂贵的操作,为什么会呢!? 如果你反编译 e.size() 方法的实现,你会发现它只是返回 size 变量,这不是一个“昂贵”的操作。 - Andrea Bori
@AndreaBori 这只是一个例子。当然,对于简单的List来说,这不是一项昂贵的操作。但最好的做法是不要在for循环的条件部分使用方法调用。 - Moritz Petersen

4
如果您检查 LinkedList 类的 size() 实现,您会发现在向列表中添加或删除元素时大小会增加或减少。调用 size() 方法只返回此属性的值,不涉及任何计算。
因此,直接调用 size() 方法应该更好,因为您将节省另一个整数的存储空间。

2

是的,第二种形式稍微更有效率,因为您不需要重复执行size()方法调用。编译器擅长自行进行此类优化。

然而,这不太可能成为您的应用程序的性能瓶颈。避免过早优化。首先使您的代码整洁易读。


1

第二个方法更好,因为在第一个块中,您正在调用e.size(),这是循环中的操作,对JVM来说是额外的负担。


1
最佳实践建议首先编写最易读的代码,而不是微观优化... - Jon Skeet
感谢@JonSkeet。但在许多地方,它经常被误读或误解。我也认为最佳实践意味着编写对JVM来说太轻的代码。 - UVM

1

在大多数情况下,HotSpot将从循环中移动e.size()。因此,它只会计算列表的大小一次。

至于我,我更喜欢以下表示法:

for (Object elem: e) {
   //Do something
}

1

我认为这样会更好一些。 也许可以避免每次初始化int变量。

List e = {element1, element2, ...., elementn};
int listSize = e.size();
int i=0;
for(i = 0; i < listSize; i++){//Do something in here
};

0

我不太确定,但我认为Java的优化器将使用静态值替换该值,因此最终结果将是相同的。


0

我认为现在我们有一个倾向,即编写简短易懂的代码,因此第一种选择更好。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接