1

When I'm making a for loop or a foreach loop, is there an efficiency difference between doing them in these 2 different ways?

For a for loop:

 int x = myArray.size();

 for(int ind = 0; ind<x; ind++){
 //do stuff
 }

vs

 for(int ind = 0; ind<myArray.size(); ind++){
 //do stuff

 }

for a foreach loop:

   for(String str: myClass.getStringList()){}

vs

   ArrayList<String>list = myClass.getStringList();

   for(String str: list){
   }

I know this is really two different questions but i think they're similar enough to justify being in the same question- correct me if I'm wrong

user9832813
  • 115
  • 1
  • 6
  • 3
    These are micro-optimizations. The motivation for the preferred approach should be readability rather than performance in the above examples.. – Chetan Kinger Aug 08 '19 at 14:00
  • 2
    Do each a million times, measure, get the average, compare. – Some programmer dude Aug 08 '19 at 14:02
  • `for` there is a difference (measurable?) since the `size` method is called each iteration; `for-each` loop is almost the same, `getStringList` is called only once ([14.14.2. The enhanced for statement](https://docs.oracle.com/javase/specs/jls/se12/html/jls-14.html#jls-14.14.2)) – user85421 Aug 08 '19 at 14:23

3 Answers3

0

The below case is better as it calculates the size of the array only once and it is used for checking inside the for look.

int x = myArray.size();

 for(int ind = 0; ind<x; ind++){
 //do stuff
 }

The following is not an efficient case as compared to the above case as it calculates the size of the array every time when the for loop runs.

for(int ind = 0; ind<myArray.size(); ind++){
 //do stuff

 }

In case of generics like

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

it is always recommended to use like this which you have already mentioned.

for(String str: list){
   }
Sambit
  • 7,625
  • 7
  • 34
  • 65
  • 1
    Are you sure this is true? arraylist.size() I thought was constant. – SedJ601 Aug 08 '19 at 14:17
  • 1
    https://stackoverflow.com/questions/863469/what-is-the-time-complexity-of-a-size-call-on-a-linkedlist-in-java – SedJ601 Aug 08 '19 at 14:19
  • In case of a for loop, it is internally calculated every time which I have mentioned. – Sambit Aug 08 '19 at 14:20
  • `In case of generics` - what generics? This example has nothing to do with generics. But you should mention that `for-each` uses an iterator underneath. – Michał Krzywański Aug 08 '19 at 14:21
  • @Sedrick, you can check this link.https://stackoverflow.com/questions/8452317/do-loops-check-the-array-length-every-time-when-comparing-i-against-array-length – Sambit Aug 08 '19 at 14:22
  • 1
    @Sambit That link is about Javascript though. – Nexevis Aug 08 '19 at 14:25
  • I have to agree with @Nexevis. That's for Javascript. Are we sure the same holds true for Java? – SedJ601 Aug 08 '19 at 14:26
  • 2
    @Sedrick I believe you are right that it is not _calculated_ every loop, so his wording is off, but I believe it is slightly faster due to not calling the method each time to get the value back but rather a local value, but that is extremely minor of an optimization. Though if you use the length again in the loop then it is useful. – Nexevis Aug 08 '19 at 14:27
  • @Nexevis, it will be good if you edit my answer, I will be thankful to all of you for nice comments. – Sambit Aug 08 '19 at 14:28
  • but I wonder why `arraylist.size()` is a constant.... `length` of an array is constant, but the size of a list can be changed – user85421 Aug 08 '19 at 14:30
  • I am guessing that anytime the size of an array changes the length variable is updated. – SedJ601 Aug 08 '19 at 14:32
  • @Carlos Heuberger I think Sedrick meant it is a class variable that is set every time the arraylist changes, not that it is declared as `final`. Confusing choice of words. I looked at the implementation and size is stored in `ArrayList` as `private int size;` – Nexevis Aug 08 '19 at 14:32
  • but that sure is not a constant, at least the method must be called every time... and, on second to last comment, arrays have constant size (and `length` is not method just a field) – user85421 Aug 08 '19 at 14:33
  • @Carlos Heuberger Yeah it is not a constant thats why I said confusing choice of words. He just meant that is does not calculate every time `size()` is called. – Nexevis Aug 08 '19 at 14:34
  • My original statement said that `ArrayList.size()` is constant. Which is true. – SedJ601 Aug 08 '19 at 14:34
  • 1
    @Sedrick A constant would be `private static final int size`, no? Which it is not. – Nexevis Aug 08 '19 at 14:35
  • 1
    why is it constant? one of the reasons to use list is that size is changed (just add/remove an element) are we speaking of Java?? First sentence (word) of API documentation of `ArrayList`: "*Resizable-array implementation of the List interface*" – user85421 Aug 08 '19 at 14:35
  • Meant more like time complexity is o(1). – SedJ601 Aug 08 '19 at 14:38
0

If .size() and .getStringList() methods are constant there is no difference between them. We say that they are constant if no calculation is required every time is called (for example if say is stored as integer instead count all the elements of the list every time)

But just as advice: if performance is not a current requirement forget it. Prioritize readility always if the code is not a proof of concept.

Pau Trepat
  • 697
  • 1
  • 6
  • 24
0

You can compile the methods and compare their bytecode to see the differences.

The biggest difference is between func1 and func2 where func1 only calls size() once, whereas func2 re-calls size() for every iteration. If size() is not cached (most implementations do cache it) then it could be costly to re-compute it. Even in that case the jitter can probably optimize it, especially with a local variable, since it might realize that the list does not change. But if the list were an exposed member then it might need to re-compute the size each time since a separate thread could have modified the list, though it's still very likely to be optimized to assume that the size has not changed.

The only difference between func3 and fun4 is that func4 uses an extra astore and aload since it's storing the list in a local variable, which can probably be optimized away by the jitter.

Ultimately the best way to test efficiency is to measure it.


private void func1() {
    ArrayList<String> list = new ArrayList<>();
    int x = list.size();
    for (int i = 0; i < x; i++)
        System.out.println(list.get(i));
}

private void func2() {
    ArrayList<String> list = new ArrayList<>();
    for (int i = 0; i < list.size(); i++)
        System.out.println(list.get(i));
}

private void func3() {
    for (String str : new ArrayList<String>())
        System.out.println(str);
}

private void func4() {
    ArrayList<String> list = new ArrayList<>();
    for (String str : list)
        System.out.println(str);
}

private void func1();
  ... storing the list ...
   8  aload_1 [list]
   9  invokevirtual java.util.ArrayList.size() : int [18] // compute list.size()
  12  istore_2 [x] // store list.size()
  13  iconst_0 
  14  istore_3 [i]
  15  goto 35
  ... print ...
  32  iinc 3 1 [i] // i++
  35  iload_3 [i] // load i
  36  iload_2 [x] // load the already computed list.size()
  37  if_icmplt 18 // if i < list.size() goto 18
  40  return

private void func2();
  ... storing the list ...
   8  iconst_0
   9  istore_2 [i]
  10  goto 30
  ... print ...
  27  iinc 2 1 [i] // i++
  30  iload_2 [i] // load i
  31  aload_1 [list] // load the list
  32  invokevirtual java.util.ArrayList.size() : int [18] // compute list.size()
  35  if_icmplt 13 // if i < list.size() goto 13
  38  return

private void func3();
  ... storing the list ...
   7  invokevirtual java.util.ArrayList.iterator() : java.util.Iterator [50]
  10  astore_2
  11  goto 31
  14  aload_2
  15  invokeinterface java.util.Iterator.next() : java.lang.Object [54] [nargs: 1]
  ... print ...
  31  aload_2
  32  invokeinterface java.util.Iterator.hasNext() : boolean [60] [nargs: 1]
  37  ifne 14
  40  return

private void func4();
  ... storing the list ...
   7  astore_1 [list] // these are the only extra operations 
   8  aload_1 [list]  // to store the list in a local variable
   9  invokevirtual java.util.ArrayList.iterator() : java.util.Iterator [50]
  12  astore_3
  13  goto 33
  16  aload_3
  17  invokeinterface java.util.Iterator.next() : java.lang.Object [54] [nargs: 1]
  ... print ...
  33  aload_3
  34  invokeinterface java.util.Iterator.hasNext() : boolean [60] [nargs: 1]
  39  ifne 16
  42  return
xtratic
  • 4,600
  • 2
  • 14
  • 32