After a rather interesting StackOverflow question, I got thinking about different types of efficiency in Java and, consequently, Android.

First, I did a basic experiment with these codes:

// Code 1
int i = 0;
Integer[] array = { 1, 2, 3, 4, 5 };
for (Integer obj : array) {
    i += obj;
}
System.out.println(i);

// Code 2
int i = 0;
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
for (Integer obj : list) {
    i += obj;
}
System.out.println(i);

I then analyzed the bytecode of each; both decompilations can be found here for easy reference.

First decompilation:

(I)Ljava/lang/Integer;
      40: aastore
      41: astore_2
      42: aload_2
      43: dup
      44: astore        6
      46: arraylength
      47: istore        5
      49: iconst_0
      50: istore        4
      52: goto          71
      55: aload         6
      57: iload         4
      59: aaload
      60: astore_3
      61: iload_1
      62: aload_3
      63: invokevirtual #22                 // Method java/lang/Integer.intValue
:()I
      66: iadd
      67: istore_1
      68: iinc          4, 1
      71: iload         4
      73: iload         5
      75: if_icmplt     55
      78: getstatic     #26                 // Field java/lang/System.out:Ljava/
io/PrintStream;
      81: iload_1
      82: invokevirtual #32                 // Method java/io/PrintStream.printl
n:(I)V
      85: return

Second decompilation:

erator:()Ljava/util/Iterator;
      71: astore        4
      73: goto          94
      76: aload         4
      78: invokeinterface #35,  1           // InterfaceMethod java/util/Iterato
r.next:()Ljava/lang/Object;
      83: checkcast     #20                 // class java/lang/Integer
      86: astore_3
      87: iload_1
      88: aload_3
      89: invokevirtual #41                 // Method java/lang/Integer.intValue
:()I
      92: iadd
      93: istore_1
      94: aload         4
      96: invokeinterface #45,  1           // InterfaceMethod java/util/Iterato
r.hasNext:()Z
     101: ifne          76
     104: getstatic     #49                 // Field java/lang/System.out:Ljava/
io/PrintStream;
     107: iload_1
     108: invokevirtual #55                 // Method java/io/PrintStream.printl
n:(I)V
     111: return

As you can see, the latter (the generic List iterator) has calls to fields and methods that the first one does not. This is because the first is optimized to run just like a basic for ( ... ) { ... } loop; it loops through every element and acts upon it. There is a lot more overhead associated with a non-optimized iterator.

From there, I was interested as to the time complexities of each method. In order from fastest to slowest:

  1. Java array iteration
  2. Java array for-loop
  3. Java generic List for-loop
  4. Java generic List iteration

So, when running the following code:

long time;
int limit;
List<Integer> list = new ArrayList<>();
Integer[] array = new Integer[1000000];
for (int j = 0; j < array.length; j++) {
    list.add(j);
    array[j] = j;
}

time = System.currentTimeMillis();
for (Integer obj : array) {
    obj.intValue();
}
System.out.println("1) Time taken is: "+(System.currentTimeMillis() - time));

time = System.currentTimeMillis();
for (Integer obj : list) {
    obj.intValue();
}
System.out.println("2) Time taken is: "+(System.currentTimeMillis() - time));

time = System.currentTimeMillis();
limit = list.size();
for (int j = 0; j < limit; j++) {
    array[j].intValue();
}
System.out.println("3) Time taken is: "+(System.currentTimeMillis() - time));

time = System.currentTimeMillis();
limit = list.size();
for (int j = 0; j < limit; j++) {
    list.get(j).intValue();
}
System.out.println("4) Time taken is: "+(System.currentTimeMillis() - time));

The time outputs (in ms) are:

1) Time taken is: 6
2) Time taken is: 20
3) Time taken is: 9
4) Time taken is: 15

Interestingly enough, these can all be explained by optimization. The array iteration, which is almost identical to the traversal, uses more native speed than the traversal, though their logic is congruent. The list for-loop requires no extraneous calls, since we handle all that ourselves (nicely enough!), and the list iterator does have this overhead, placing it in last.

So next time you're running a list iteration 60 times a second, you'll know why your program is lagging an extra 300ms!

Next Post Previous Post