进行循环优化

List<String> flowers = new ArrayList<String>(); 

我的for循环目前看起来像这样…

 for (int i = 0; i < flowers.size(); i++) { ... } 

或者我应该改变它看起来像下面给出的代码

 int size = flowers.size(); for (int i = 0; i < size; i++) { ... } 

这是更高性能(假设我有一大堆花),我猜它应该是后者。

最好使用for-each循环 [更具可读性]

 for (Flower flower :flowers){ //... } 

我已经转储使用javap指令以下代码:

 public void forLoop1() { List<String> lst = new ArrayList<String>(); for (int i = 0; i < lst.size(); i++) { System.out.println("hi"); } } public void forLoop2() { List<String> lst = new ArrayList<String>(); int size = lst.size(); for (int i = 0; i < size; i++) { System.out.println("hi"); } } 

 public void forLoop1(); Code: 0: new #2; //class java/util/ArrayList 3: dup 4: invokespecial #3; //Method java/util/ArrayList."<init>":()V 7: astore_1 8: iconst_0 9: istore_2 10: iload_2 11: aload_1 12: invokeinterface #4, 1; //InterfaceMethod java/util/List.size:()I 17: if_icmpge 34 20: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream; 23: ldc #6; //String hi 25: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/Str ing;)V 28: iinc 2, 1 31: goto 10 34: return public void forLoop2(); Code: 0: new #2; //class java/util/ArrayList 3: dup 4: invokespecial #3; //Method java/util/ArrayList."<init>":()V 7: astore_1 8: aload_1 9: invokeinterface #4, 1; //InterfaceMethod java/util/List.size:()I 14: istore_2 15: iconst_0 16: istore_3 17: iload_3 18: iload_2 19: if_icmpge 36 22: getstatic #5; //Field java/lang/System.out:Ljava/io/PrintStream; 25: ldc #6; //String hi 27: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/Str ing;)V 30: iinc 3, 1 33: goto 17 36: return 

它不会为我优化。

java版本“1.6.0_22”Java™SE运行时环境(build 1.6.0_22-b04)Java HotSpot™客户端虚拟机(构build17.1-b03,混合模式,共享)

所以,如果你需要从提到的两个select,第二,但我个人会去为for-each


为每个performance

来自Joshua Bloch的Effective Java中的第46项:

在版本1.5中引入的for-each循环通过完全隐藏迭代器或索引variables,摆脱了混乱和错误的可能性。 由此产生的习惯用法同样适用于集合和数组:

 // The preferred idiom for iterating over collections and arrays for (Element e : elements) { doSomething(e); } 

当看到冒号(:)时,将其读为“in”。因此,上面的循环读取为“对于元素中的每个元素e”。请注意,使用for-each循环没有性能损失,即使对于数组。 实际上,在某些情况下,它可能会比普通的for循环提供轻微的性能优势,因为它只计算一次数组索引的极限。 虽然你可以手工做(项目45),程序员并不总是这样做。


也可以看看

  • IS-有-A-性能差之间-A-for循环和-A-的for-each循环

很抱歉地说,但@ Jigar的答案是不正确的。 这是正确的答案。 (tldr;不要for : each )。

 import java.util.ArrayList; import java.util.List; public class LoopTest { public static void main(String s[]) { long start, end; List<Integer> a = new ArrayList<Integer>(); for (int i = 0; i < 2500000; i++) { a.add(i); } ///// TESTING FOR : EACH LOOP start = System.currentTimeMillis(); for (Integer j : a) { int x = j + 3; } end = System.currentTimeMillis(); System.out.println(end - start + " milli seconds for [ Integer j : a ] "); ////// TESTING DEFAULT LOOP start = System.currentTimeMillis(); for (int i = 0; i < a.size(); i++) { int x = a.get(i) + 3; } end = System.currentTimeMillis(); System.out.println(end - start + " milli seconds for [ int i = 0; i < a.length; i++ ] "); ////// TESTING SLIGHTLY OPTIMIZED LOOP start = System.currentTimeMillis(); int size = a.size(); for (int i = 0; i < size; i++) { int x = a.get(i) + 3; } end = System.currentTimeMillis(); System.out.println(end - start + " milli seconds for [ int i = 0; i < size; i++ ] "); //// TESTING MORE OPTIMIZED LOOP start = System.currentTimeMillis(); for (int i = size; --i >= 0;) { int x = a.get(i) + 3; } end = System.currentTimeMillis(); System.out.println(end - start + " milli seconds for [ int i = size; --i >= 0; ] "); } } 

结果:

 96 milli seconds for [ Integer j : a ] 57 milli seconds for [ int i = 0; i < a.length; i++ ] 31 milli seconds for [ int i = 0; i < size; i++ ] 31 milli seconds for [ int i = size; --i >= 0; ] 

你可以做出自己的想法,但是对JVM优化器给予了太多的归因。 你仍然必须对自己的代码很聪明,并且使用for : each符号都不是一个好主意(几乎都是)。 正如你所看到的,你有一个好主意,把大小放在自己的variables中。

即使这些优化中的一些可能依赖于JVM(有些可能与JIT一起使用),但了解Java做什么以及Java不做什么是很重要的。

JVM无法对其进行优化,因为size()是一个方法,JVM不能(也不会试图)确定size()在这个上下文中总是返回相同的值。 提供的size()值不会改变,第二个是更高性能的,但增益是如此轻微,以至于您甚至不必考虑使用它。

如果性能至关重要,则使用简单的计数器循环,但对于98%的情况,代码的清晰度和简单性要重要得多(比如1000倍或更多),并且应该使用for-each循环。

@David指出,使用计数器速度更快,但我要指出,即使对于10,000个条目,差异也是亚微秒。

如果你有超过10,000个条目的集合,那么很可能你不应该在每一个可能的情况下进行蛮力的迭代。 对于你想要的任何东西来说,更像是一个像Map一样查找的集合是更好的解决scheme。 如果您的项目数量less于10,000,性能不太可能变得重要。

如果数组列表在迭代时发生更改,则行为会有所不同。 但是我想你不这样做。 根据我的testing,后者通常更快(特别是在像Android这样的系统上)。 我会写如下:

 for (int i = 0, size = flowers.size(); i < size; i++) { ... } 

从Java语言规范(14.14.1):

基本的for语句执行一些初始化代码, 然后 重复 执行一个Expression ,一个Statement和一些更新代码直到Expression的值为false。

expression式是i < flowers.size()在第一个例子中的i < flowers.size() ,并在每次迭代中计算一次。 在你的特殊情况下,它不应该有明显的区别,因为ArrayList上的flowers.getSize()是一个很短的方法。 但是,一般来说,如果expression式的结果对于每次迭代是相同的并且是昂贵的,则进行预先计算。

结果 :这必须在Java虚拟机的每个实现中产生相同的输出,并且certificateExpression在每次迭代中都会被评估一次:

 int counter2 = 10; for (int counter1 = 0; counter1 < counter2; counter1++) { System.out.println(counter1 + ", " + counter2); counter2--; } 

输出:

 0, 10 1, 9 2, 8 3, 7 4, 6 

最好的select是

 [ int i = 0; i < size; i++ ] 

你的结果会根据哪个JVM和其他设置(如-client和-server)而有所不同,因为某些测量非常小,你需要使用nano秒进行测量,而且你需要做很多testing,否则你最终会被GC搞乱结果。 这些testing也有JVM优化循环的习惯。 我试图通过将代码末尾的variables放在屏幕上来消除这种风险。

 1.6 -server 7.968242071 milli seconds for [ Integer j : a ] 7.206275775999999 milli seconds for [ int i = 0; i < a.length; i++ ] 1.5864E-5 milli seconds for [ int i = 0; i < size; i++ ] 14.774186076999998 milli seconds for [ int i = size; --i >= 0; ] -client 83.36101683999999 milli seconds for [ Integer j : a ] 44.288568631 milli seconds for [ int i = 0; i < a.length; i++ ] 2.3191E-5 milli seconds for [ int i = 0; i < size; i++ ] 24.826621246 milli seconds for [ int i = size; --i >= 0; ] 1.7 -server 7.029150422 milli seconds for [ Integer j : a ] 6.6269827779999995 milli seconds for [ int i = 0; i < a.length; i++ ] 1.3852E-5 milli seconds for [ int i = 0; i < size; i++ ] 13.842110377 milli seconds for [ int i = size; --i >= 0; ] 13.868426141 milli seconds for [ int i = a.size()-1; i >= 0; i-- ] 1.6618000000000003E-5 milli seconds for [ int i = 0; i < a.size(); i++ ] -client 7.382479727 milli seconds for [ Integer j : a ] 6.748068759 milli seconds for [ int i = 0; i < a.length; i++ ] 1.4162999999999998E-5 milli seconds for [ int i = 0; i < size; i++ ] 13.951547335999999 milli seconds for [ int i = size; --i >= 0; ] 13.929234053999998 milli seconds for [ int i = a.size()-1; i >= 0; i-- ] 1.6873E-5 milli seconds for [ int i = 0; i < a.size(); i++ ] 

testing代码:

 public static void main(String s[]) { long start=0, end = 0, delta = 0; //int[] a = new int[2500000]; List<Integer> a = new ArrayList<Integer>(); int x = 0; for (int i = 0; i < 2500000; i++) { a.add(i); } start=0; end = 0; delta = 0; for (int ctr = 0; ctr < 1000; ctr++) { start = System.nanoTime(); for (Integer j : a) { x = j + 3; } end = System.nanoTime(); delta += end - start; } System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ Integer j : a ] "); start=0; end = 0; delta = 0; for (int ctr = 0; ctr < 1000; ctr++) { start = System.nanoTime(); for (int i = 0; i < a.size(); i++) { x = a.get(i) + 3; } end = System.nanoTime(); delta += end - start; } System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = 0; i < a.length; i++ ] "); int size = a.size(); start=0; end = 0; delta = 0; for (int ctr = 0; ctr < 1000; ctr++) { start = System.currentTimeMillis(); for (int i = 0; i < size; i++) { x = a.get(i) + 3; } end = System.currentTimeMillis(); delta += end - start; } System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = 0; i < size; i++ ] "); start=0; end = 0; delta = 0; for (int ctr = 0; ctr < 1000; ctr++) { start = System.nanoTime(); for (int i = size; --i >= 0;) { x = a.get(i) + 3; } end = System.nanoTime(); delta += end - start; } System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = size; --i >= 0; ] "); start=0; end = 0; delta = 0; for (int ctr = 0; ctr < 1000; ctr++) { start = System.nanoTime(); for (int i = a.size()-1; i >= 0; i--) { x = a.get(i) + 3; } end = System.nanoTime(); delta += end - start; } System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = a.size()-1; i >= 0; i-- ] "); start=0; end = 0; delta = 0; for (int ctr = 0; ctr < 1000; ctr++) { start = System.currentTimeMillis(); for (int i = 0; i < a.size(); i++) { x = a.get(i) + 3; } end = System.currentTimeMillis(); delta += end - start; } System.out.println(Math.pow(10, -6) * delta / 1000 + " milli seconds for [ int i = 0; i < a.size(); i++ ] "); System.out.println(x); } 

这只是通过实例来说明情况。

我testing了for (int i = 0; i < list.size(); i++)循环的“正常”执行和for (int i = -1, size = list.size(); ++i < size;) 。 我在eclipse中从命令行运行testing,注意到巨大的差异。

在eclipse中运行的结果:

 Time for Original: 32552 ms Time for MicroOptimized 32707 ms Fastest Loop: Original Slowest loop takes 0.47616121897272057% more time 

从命令行运行的结果:

 Time for Original: 274489 ms Time for MicroOptimized 30516 ms Fastest Loop: MicroOptimized Slowest loop takes 799.4920697339101% more time 

所以在eclipse中,两个for循环占用相同的时间,但是从命令行运行时,原始版本比微优化版本花费的时间多800%。 差异的程度让我感到震惊。 我猜这个eclipse使用了一个不同的JVM,它应用了一些聪明的优化技巧。

这并不意味着你应该开始使用microoptimized版本。 在几乎所有情况下,迭代的列表可能会很小,性能差异可以忽略不计。 而使用标准版本所获得的可读性,每个人都会更快地认识和理解,这比一个不可知的性能增长更有益处。

为了完整性,这是我跑的代码:

 public static void main(String[] args) { List<Byte> list = initializeList(); byte value = 0; final int NUM_LOOPS = 100; long startOriginal, startOptimized, endOriginal, endOptimized; startOptimized = System.currentTimeMillis(); for (int j = 0; j < NUM_LOOPS; j++) { for (int i = -1, size = list.size(); ++i < size;) { value = list.get(i); } } endOptimized = System.currentTimeMillis(); startOriginal = System.currentTimeMillis(); for (int j = 0; j < NUM_LOOPS; j++) { for (int i = 0; i < list.size(); i++) { value = list.get(i); } } endOriginal = System.currentTimeMillis(); System.out.println(value); printResults(startOriginal, endOriginal, startOptimized, endOptimized); } private static void printResults(long startOriginal, long endOriginal, long startOptimized, long endOptimized) { long timeOriginal = endOriginal - startOriginal; long timeOptimized = endOptimized - startOptimized; long diff = Math.abs(timeOriginal - timeOptimized); long min = Math.min(timeOriginal, timeOptimized); System.out.println("Time for Original: " + timeOriginal + " ms" + " Time for MicroOptimized " + timeOptimized + " ms"); System.out.println("Fastest Loop: " + ((timeOriginal < timeOptimized) ? "Original" : "MicroOptimized")); System.out.println("Slowest loop takes " + ((double) 100 * diff / min) + "% more time"); } public static List<Byte> initializeList(){ List<Byte> list = new ArrayList<Byte>(); final Byte ONE = new Byte((byte) 1); for (int i = 0; i < Integer.MAX_VALUE / 10; i++) { list.add(ONE); } return list; } 

}

任何一个都可以。 取决于JVM,第二个可能会快几个时钟周期,但这将是一个不可估量的或微不足道的差异。 注意这些types的子优化。 除非您正在构build一个实时系统,而每个CPU计数器都会计数,否则只会增加错误的复杂性和来源。

我会build议使用迭代器构造(如已经build议)

 For (Flower flower: flowers) { ... 

这是清晰,灵活和可预测的。

我的方法在这个问题上有点不同。 对我而言,select哪种方法确实无关紧要。 原因是你将会得到最佳优化方法的“性能提升”将会达到250万次迭代的50ms! (根据@大卫的post)。 显然,这种改进不是像你想浪费你的宝贵时间寻找优化的解决scheme。

(但是,按照OP的原始问题,我还想提出最后一种方法。)

我知道答案有点奇怪和不常见,但这是现实。

为了避免所有这些编号和迭代器和检查在编写代码中使用以下最简单的最易读的代码,其性能最高。 为什么这有最大的性能(细节即将到来)

 for (Object object : aCollection) { // Do something here } 

如果需要索引,则:在上述两种forms之间进行select:第二种是更好的,因为您使用本地variables进行检查。 当方法退出时,variables就会被转移到堆栈的垃圾桶中。

此外,如果您想知道使用方法调用作为源集合是否有任何性能影响。 那就是 – 方法会被多次调用 – 答案是否定的。 这是一个例子:

 import java.util.*; public class TestForeach { public static void main (String[] args) { for (String s : getStrings()) { System.out.println("The string was: "+s); } } private static List<String> getStrings() { System.out.println("IN GET STRINGS"); return Arrays.asList("A","B","C"); } } 

这将导致:

 IN GET STRINGS The string was: A The string was: B The string was: C 

因此,该方法将只被调用一次。

第一个只是因为两者都是相同的,但是在第二个代码片段中创build一个更多的redundent intvariables。

所以去第一个代码片段

 for (int i = 0; i < flowers.size(); i++) { ... } 
 String d = JOptionPane.showInputDialog("enter start"); int s = Integer.parseInt(d); String h = JOptionPane.showInputDialog("enter the end"); int z = Integer.parseInt(h); for (int a = 1 ; a<10 ; a++) { if (a%2 == 0 ) { JOptionPane.showMessageDialog(null, a); System.out.print(a); } }