1

First 2 examples:

1)
MyClass myClass;
for (int i=0; i<arrayList.size(); i++) {
    myClass = arrayList.get(i);
    ...
}


2)
for (int i=0; i<arrayList.size(); i++) {
    MyClass myClass = arrayList.get(i);
    ...
}

In the first example the reference variable myClass is created only once. But what about in the second example, is it created only once, or once for each iteration? My thought is perhaps the compiler optimizes this, I don't know.

I tried to answer this question by coding an example, but couldn't figure it out. How can it be proven via code?

Note: I realize example 2 is better style since myClass isn't known outside of the for loop, and it's scope is kept to a minimum. I also have searched here but haven't found a definitive answer to this exact question (usually it's a question of "which is preferred?".) I also assume that if the myClass reference is created each iteration it isn't a big performance issue.

Edit: Again, I am not asking which is better coding style. Also, I wonder if it can be deduced/proven via code. I tried to generate and compare the bytecode, but I am not familiar with bytecode and what was generated was not an exact match.

  • I would guess that it doesn't matter, it is a micro optimization which is not really needed – Lino Oct 26 '17 at 12:42
  • 3
    I suggest you check the generated bytecode, I think you will find they are identical (IIRC, I didn't double check). – Mark Rotteveel Oct 26 '17 at 12:43
  • When you declare a variable, you don't "create" anything. Variables are just a convenient name to help you remember where you put some result, and allow you to control where that result can be used, and associate some type information with that result. – Andy Turner Oct 26 '17 at 12:48
  • @AxelH but why not declare it in the loop? You can't use it outside the loop (because of definite assignment); and you should always declare variables in the tightest possible scope. – Andy Turner Oct 26 '17 at 12:54
  • @MarkRotteveel I tried this and looked at bytecode for the first time last night. They did not seem identical, very minor differences, but I didn't really understand the differences, so I thought I would ask here. – itDontMeanAThing Oct 26 '17 at 13:01
  • @Lino As mentioned, I'm not asking about optimization. – itDontMeanAThing Oct 26 '17 at 13:02
  • @itDontMeanAThing you may be referring to the differences I describe in the (*) in my answer. – Andy Turner Oct 26 '17 at 13:02
  • @AlexSaunin I read that, and the topmost answer mentions the variable will be created for each iteration, and the bytecode answer says they are identical. I don't really understand bytecode, so I couldn't verify that. Also, no mention of how to prove via code (if that's possible). – itDontMeanAThing Oct 26 '17 at 13:04
  • @AndyTurner But there must be space put aside to hold the reference (and maybe additional information, such as the reference type) to the myClass object. Can you elaborate? – itDontMeanAThing Oct 26 '17 at 13:06
  • @itDontMeanAThing that memory only needs to be put aside when you write to and/or can read from that variable. Because of Java's definite assignment rules, you can't read from the variable outside the loop when you declare it outside, so it knows it only needs to put that memory aside for the body of the loop. – Andy Turner Oct 26 '17 at 13:10
  • Also worth exploring is if you put two loops like that back to back, I suspect it will reuse the memory space if possible – phflack Oct 26 '17 at 13:12
  • @AndyTurner So interesting. Things like this are confusing to those like me, who are somewhat familiar with java but don't know the intricacies. From doing some searching here, what I wondered is often incorrectly understood. – itDontMeanAThing Oct 26 '17 at 13:18
  • @itDontMeanAThing most of the time, you don't need to worry about it. Just focus on writing clear code. – Andy Turner Oct 26 '17 at 13:21
  • @AndyTurner Yes, of course, and I would use the second example in real life. But I also like to simply ponder how things work, and for some reason this question came up and I started thinking about it and couldn't let it go. – itDontMeanAThing Oct 26 '17 at 13:23

1 Answers1

6

When you declare a variable, you don't "create" anything. Variables are just a convenient name to help you remember where you put some result, and allow you to control where that result can be used, and associate some type information with that result; but once you compile the code, they no longer exist.

Compare the bytecode (*):

void outside(int i, List<?> list) {
  Object obj;
  for (i = 0; i < list.size(); i++) {
    obj = list.get(i);
  }
}

void inside(int i, List<?> list) {
  for (i = 0; i < list.size(); i++) {
    Object obj = list.get(i);
  }
}

Decompiles to:

  void outside(int, java.util.List<?>);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: aload_2
       4: invokeinterface #2,  1            // InterfaceMethod java/util/List.size:()I
       9: if_icmpge     26
      12: aload_2
      13: iload_1
      14: invokeinterface #3,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      19: astore_3
      20: iinc          1, 1
      23: goto          2
      26: return

  void inside(int, java.util.List<?>);
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: aload_2
       4: invokeinterface #2,  1            // InterfaceMethod java/util/List.size:()I
       9: if_icmpge     26
      12: aload_2
      13: iload_1
      14: invokeinterface #3,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      19: astore_3
      20: iinc          1, 1
      23: goto          2
      26: return

The two are identical. Use the one which is most readable.


(*) I've declared the loop variable i as a parameter, in order to avoid any differences in the generated bytecode caused by the Object being declared before or after i. The only difference this makes to the bytecode is in the aload_N/iload_N/astore_N/istore_N instructions - the N is different because the variables are stored in different "slots". This is not a significant difference.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • Curiously, I also tried this, but don't have a way to read the bytecode/do a proper diff on the generated files. Instead I checked the file sizes of the different variations (defined before loop, defined and set to null before loop, and in the loop), and all of the variations have different file sizes (519, 508, and 507 bytes respectively) – phflack Oct 26 '17 at 13:01
  • @phflack `javap -c`. – Andy Turner Oct 26 '17 at 13:01
  • Thanks, just checked the bytecode for each of the variations, it looks like it's 2 instructions longer for setting null to start with, otherwise the same (if the loop counter is defined before the object, looks like that's what changed my test (the 519 byte file was the null)) – phflack Oct 26 '17 at 13:09
  • @AndyTurner This is very interesting and helpful. Thank you. I don't really understand the bytecode, but I accept your explanation of it. What I am curious about now is how the reference (and any associated information, such as the reference type), don't really exist. – itDontMeanAThing Oct 26 '17 at 13:13
  • @itDontMeanAThing I won't claim to be an expert, but it's not really very complicated. TBH, I rarely look at it for "real" work; I use it mostly for answering questions like this on SO. – Andy Turner Oct 26 '17 at 13:16
  • Note I didn't say the *reference* doesn't exist, I said the *variable* doesn't exist. – Andy Turner Oct 26 '17 at 13:17
  • @AndyTurner I'll have to think about the difference between the reference and the variable. Bytecode doesn't look too bad, but I only looked at it for the first time last night when pondering this. – itDontMeanAThing Oct 26 '17 at 13:20