Java for循环最佳实践

8
什么是在需要索引的情况下遍历数组的最佳方法?
选项1:
int len = array.length;
for (int i = 0; i < len; ++i) {
    array[i] = foo(i);
}

选项2:

for (int i = 0; i < array.length; i++) {
    array[i] = foo(i);
}

那么,这是否重要呢?或者有更好的方法来做到这一点吗? 只是指出区别:在一个情况下,数组的长度在循环中作为测试的一部分进行评估,尽管编译器通常应该优化它。


其次,在这里++ii++有什么不同吗?如果是C ++,我肯定更喜欢++i,但对于Java不确定。


3
这些不应该有任何区别。 - user1675187
4个回答

6
一般来说,这两种方法是等效的。需要注意的是,在 HTML 中,< 和 & 需要进行转义处理。
for (int i = 0 ; i < foo() ; i++) {
    ...
}

foo()在每次迭代之前都会被调用一次(而不是仅在第一次迭代之前调用一次),因此对于更复杂的情况,您可能需要考虑这一点,比如可以采取以下措施:

int n = foo();
for (int i = 0 ; i < n ; i++) {
    ...
}

这个内容可以类比你的选项1。因此我会说选项1肯定是两者中更加安全的,但大多数情况下,你使用哪个都不会有显著的差别。


至于你的第二个问题:++i先将变量加一再检索其值,而i++则是先检索值再加一。只需尝试这两段代码:

int i = 0;
System.out.println(++i);
------------------------
int i = 0;
System.out.println(i++);

第一个输出 1,但是第二个输出 0。当然,当 ++ii++ 单独使用时没有区别。


我在问题中没有看到 i < foo(),它已经更新了吗? :) - Bhesh Gurung
@BheshGurung 那个 foo 函数演示了我之前提到的 for 循环条件右侧的重复计算。当你在右侧使用 array.length 这样的语句时,不会有任何影响。但是当你使用 foo 这样的语句时,应该要有一定的谨慎。 - arshajii
@arshajii 我担心在 size() 访问的情况下 foo() 会减慢速度,所以我进行了一些测试(请参见最后一个答案)。显然是相同的。编译器只是在编译时将 size() 转换为简单的变量访问吗? - Ced

6
在这种情况下,“i++”与“++i”并不重要。虽然C语言专家会告诉你将数组长度存储在一个变量中,但现代优化编译器使得在此情况下不必要,只要循环中的长度不改变即可。如果您真的担心,可以对两者进行基准测试,但由于“.length”实际上不需要每次遍历整个数组,您将不会有问题。

非常好的答案!实际上,始终使用选项1也是推荐的,不是吗? - Roy Ling
选项1是针对哪个问题?是i++还是++i?出于可读性的原因,我更喜欢使用i++,但我可以理解为什么++i可能是更好的默认选择,这样在您改变代码但没有注意到后增量时,将使用i的新值。对于那些不知道用户不关心表达式的值的愚蠢编译器来说,++i也更快。 - Dan
不,我指的是.length的选项1。对于++ii++,我认为这只是一种代码风格问题。 - Roy Ling
如果所有编译器都会进行优化,那么这只是一个风格问题。我仍然觉得选项1表明程序员关心性能,但我想也可以说它表明你没有意识到Java编译器已经为你处理了优化。 - Marshall Farrier

0
除了arshaji的回答,我想知道在循环中使用size()与提前存储它是否有性能上的优势。我相信结果表明编译器确实会优化代码,并且访问列表的长度与访问变量是相同的(我担心必须通过函数来访问会减慢速度)。
以下是不同循环方法所需的时间:
for(long i = 0 ; i < mylist.size(); i++){}
VS
for(long i = 0 ; i < 10_000_000; i++){}

这是一个包含一千万个元素的列表的结果:

fixed length:
,162,157,151,157,156,159,157,149,150,170,158,153,152,158,151,151,156,156,151,153
getSize:
,164,156,159,154,151,160,162,152,154,152,151,149,168,156,152,150,157,150,156,157



import java.util.ArrayList;
import java.util.List;

public class Main {

    final static int LENGTH_SAMPLE = 20;
    final static long LENGTH = 10_000_000;

    public static void main(String[] args) {

        List<Long> mylist = new ArrayList<>();
        for(long i = 0 ; i < LENGTH; i++){
            mylist.add(i);
        }
        System.out.println("fixed length:");
        for(int i = 0 ; i < LENGTH_SAMPLE; i++){
            System.out.printf("," + fixedSize(mylist));
        }
        System.out.println("");
        System.out.println("getSize:");
        for(int i = 0 ; i < LENGTH_SAMPLE; i++){
            System.out.printf("," + fctSize(mylist));
        }
    }

    private static long fixedSize(List list){
        long start = System.currentTimeMillis();

        for(long i = 0 ; i < LENGTH; i++){
            System.currentTimeMillis();
        }
        return System.currentTimeMillis() - start;
    }

    private static long fctSize(List list){
        long start = System.currentTimeMillis();

        for(long i = 0 ; i < list.size(); i++){
            System.currentTimeMillis();
        }
        return System.currentTimeMillis() - start;
    }
}

0
关于在for循环中是否使用"array.length": 通常编译器会进行一些优化,因此使用for循环中的变量与使用"array.length"是等效的。
关于"i++"和"++i": 在C++中,建议使用++i,因为它更高效。但在Java中,在这种情况下它们是等效的。

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