for loop optimization

2019-01-02 18:15发布

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

My for loop currently looks like this...

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

OR should I change this to look like the code given below

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

Which is more performant (assuming I have a large array of flowers), I am guessing it should be the latter.

15条回答
柔情千种
2楼-- · 2019-01-02 18:36

It is better to use for-each loop [more readable]

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

I have dumped instructions using javap for the following code:

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

It doesn't optimize for me.

java version "1.6.0_22" Java(TM) SE Runtime Environment (build 1.6.0_22-b04) Java HotSpot(TM) Client VM (build 17.1-b03, mixed mode, sharing)

So if you need to choose from mentioned two, go for second, but I personally would go for for-each.


for-each Performance

From Item 46 in Effective Java by Joshua Bloch :

The for-each loop, introduced in release 1.5, gets rid of the clutter and the opportunity for error by hiding the iterator or index variable completely. The resulting idiom applies equally to collections and arrays:

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

When you see the colon (:), read it as “in.” Thus, the loop above reads as “for each element e in elements.” Note that there is no performance penalty for using the for-each loop, even for arrays. In fact, it may offer a slight performance advantage over an ordinary for loop in some circumstances, as it computes the limit of the array index only once. While you can do this by hand (Item 45), programmers don’t always do so.


See Also

查看更多
泪湿衣
3楼-- · 2019-01-02 18:37

Either one will do. Depending on JVM, the second may be a few clock-cycles faster, but it will be an immeasurable or insignificant difference. Beware of these types of sub-optimizations. Unless you are building a real-time system, where every CPU tick counts, they just add complexity and more sources for errors.

I would suggest using the iterator construct (as has already been suggested)

For (Flower flower: flowers) { ...

It's clear, flexible and predicatble.

查看更多
裙下三千臣
4楼-- · 2019-01-02 18:40

The JVM can't optimize it because size() is a method, and JVM can't (and won't try to) determine that the size() will always return the same value in this context. Provided size() value doesn't change, the second one is slightly more performant, but the gain is so, so slight that you don't really have to even consider using it.

查看更多
不流泪的眼
5楼-- · 2019-01-02 18:40

Simple yet effective

 for (ConfigDataModel.VisaTypesBean.AddedBean visatype : visaTypesBeans) {
                            if (visatype.getId() == 24) {

                            }
查看更多
几人难应
6楼-- · 2019-01-02 18:45

Sorry to say, but @Jigar's answer is incorrect. This is the correct answer. (tldr; don't use 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; ] ");       

    }

}

The results:

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; ] 

You can make up your own mind, but too much attribution is given to the JVM optimizer. You still have to be smart with your own code, and using for : each notation is NOT a good idea (almost ever). As you can see, you have a good idea by putting size in its own variable.

Even though some of these optimization may be JVM-dependent (and some may kick in with the JIT), it's important to know what Java does and what Java doesn't do.

查看更多
泛滥B
7楼-- · 2019-01-02 18:47

If performance is critical, use the plain counter loop, however for 98% of cases, clarity and simplicity of code is far more important (like 1000x or more) and you should use a for-each loop.

@David points out that using a counter is faster, but I would point out that even for 10,000 entries, the difference is sub-microsecond.

If you have a collection of more than 10,000 entries it is highly likely you shouldn't be brute force iterating over every possibility. It is more likely a collection with a lookup like a Map is a better solution for whatever you have in mind. If you have far less than 10,000 entries performance is less likely to be important.

查看更多
登录 后发表回答