Java for 循环最佳实践

Posted

技术标签:

【中文标题】Java for 循环最佳实践【英文标题】:Java for-loop best practice 【发布时间】:2012-09-20 21:32:36 【问题描述】:

当您需要索引时,循环遍历数组的最佳方法是什么?

选项 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++ 但不确定 Java,我绝对更喜欢 ++i

【问题讨论】:

这些应该没有什么不同。 【参考方案1】:

i++++i 在这种特殊情况下无关紧要。虽然 C 大师会告诉您将 array.length 存储在变量中,但现代优化编译器在这种情况下不需要这样做,只要长度在循环中没有改变。如果您真的担心,您可以对两者进行基准测试,但由于 .length 实际上并不需要每次遍历整个数组就可以了。

【讨论】:

很好的答案!实际上,也建议始终使用选项 1,不是吗? 选项 1 哪个问题?到i++++i?出于可读性原因,我更喜欢i++,但我可以理解为什么++i 可能是一个更好的默认值,以便使用i 的新值,以防您碰巧更改了代码并且没有注意到后增量。 ++i 对于没有意识到用户不在乎表达式具有什么值的愚蠢编译器来说也更快。 不,我的意思是.length 的选项1。对于++ii++,我认为这只是代码风格问题。 如果所有编译器都会进行优化,那么无论如何这只是一个风格问题。我仍然觉得选项 1 表明编码器关心性能,但我想有人也可能会争辩说,它表明你没有意识到 Java 编译器会为你处理优化。【参考方案2】:

通常这两种方法是等价的。您应该注意,在

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 &lt; foo()。是否已更新? :) @BheshGurung foo 函数演示了如何重复评估 for 循环条件的 rhs,正如我所提到的。当您在 rhs 上有类似 array.length 的东西时,它不会有任何区别。但是当你有foo 这样的东西时,你应该在某种程度上保持谨慎。 @arshajii 我担心 foo() 在 size() 访问的情况下会减慢速度,我进行了一些测试(见最后一个答案)。显然是一样的。编译器是否只是在编译时将 size() 转换为简单的 var 访问?【参考方案3】:

for循环中是否使用“array.length”: 一般编译器会做一些优化,结果相当于在for循环中使用了一个变量

对于“i++”和“++i” 在 C++ 中,++i 是首选且更高效,但在 Java 中,它们在这种情况下是等价的。

【讨论】:

【参考方案4】:

除了 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;
    

【讨论】:

以上是关于Java for 循环最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

MATLAB for 循环索引的最佳实践

Partials vs for 循环——最佳实践

OpenMP/C++:并行 for 循环,之后减少 - 最佳实践?

Object/Map 何为最佳实践

表的 ASP.NET 最佳实践

.NET MongoDB 连接的最佳实践?