Skip to main content

Declare Variables Inside or Outside a Loop

Posted by ddevore on August 21, 2006 at 9:22 PM PDT

The question has been asked many times by many different people. Is it better to declare variables inside or outside a loop. I did a search for this recently to find support for which is better. What I found was a lot of opinions with very little actual support for either position. My position has always been that it was more readable and better for performance and memory to declare variables outside loops.

Overview

The question of readability is more religious but does have a little to do with performance. The question about memory and performance goes hand in hand and can be shown definitively. I am going to go through both questions and show the difference from the byte code level using javap. I am not a byte code expert but I will do my best to explain the code properly. If there is an error in my explanation please let me know.

Primitives

For this example I will use the following methods:


        public void testPrimitives1(int count) {

                int a = 0;

                for (int i = 0; i < count; i++) {

                        a = i;

                }

        }



        public void testPrimitives2(int count) {

                int a;

                for (int i = 0; i < count; i++) {

                        a = i;

                }

        }

   

        public void testPrimitives3(int count) {

                for (int i = 0; i < count; i++) {

                        int a = i;

                }

        }


Yes the examples are very simple. I did not want to get caught up in a lot of code in the byte codeup As you can see I am using 2 methods with the variable declared outside the loop. The reason for this will be apparent once we get to the byte code.


public void testPrimitives1(int);

      Code:

       0: iconst_0

       1: istore_2

       2: iconst_0

       3: istore_3

       4: iload_3

       5: iload_1

       6: if_icmpge 17

       9: iload_3

       10: istore_2

       11: iinc 3, 1

       14: goto 4

       17: return


Looking at the first method above you can see the first 2 lines are iconst_0 and istore_2 this is the declaration and initialization of the loop variable, you will see it in the other 2 methods also.

The next 2 lines are iconst_0 and istore_3 these are the declaration and initialization of the int a = 0 variable.

The next 3 lines are the loading of the count parameter and the loop variable and the comparison of the two.

The next 2 lines are the loading of the loop variable and storing it to the a variable.

The next line is for incrementing the loop variable.

The only thing left is the goto for the loop and the return from the method.

You can see from this example that there is not a whole lot going on here. In long hand listing you have:

  • Declaration and initialization of the loop variable.
  • Declaration and initialization of the primitive variable.
  • Load the input parameter and loop variable and compare them.
  • Load the loop variable and store it to the primitive variable.
  • Increment the loop variable.
  • Goto the beginning of the loop.



Lets now look at the next example.


public void testPrimitives2(int);

      Code:

       0: iconst_0

       1: istore_3

       2: iload_3

       3: iload_1

       4: if_icmpge 15

       7: iload_3

       8: istore_2

       9: iinc 3, 1

       12: goto 2

       15: return


Looking at this method you can see the following, in the long hand listing method:

  • Declaration and initialization of the loop variable.
  • Load the input parameter and loop variable and compare them.
  • Load the loop variable and store it to the primitive variable.
  • Increment the loop variable.
  • Goto the beginning of the loop.



The next example is:


public void testPrimitives3(int);

      Code:

       0: iconst_0

       1: istore_2

       2: iload_2

       3: iload_1

       4: if_icmpge 15

       7: iload_2

       8: istore_3

       9: iinc 2, 1

       12: goto 2

       15: return


This one is exactly as the previous one with the exception of the storage location within the current frame. This is due to not initializing the variable in the previous one therefore not requiring the istore_ command. The storage locations in the current frame is because the variables are declared in a different order.

Primitive Conclusion

Performance and Memory

The second and third methods are functionally identical because I didn't initialize the variable in the second. Comparing them with the first method we have the only difference being the initialization of the primitive used. So you can say that they are all essentially the same with the first method having the initialization of the primitive. Now if you like are like me and like variables declared outside the loop you can do it with no real fear of a performance hit. If you like it declared outside the loop and want the best possible performance you can declare it outside and not initialize it. Since I don't like uninitialized variables I will take the small performance hit to have my variables declared outside the loop.

Readability

I have always liked my variables declared outside the loop because I like the ability to look at the top of the loop to see what is used within the loop instead of looking through the loop. I do understand the arguments for declaring them inside the loop but would rather not make that change, see conclusion for explanation. What I do is if I am modifying existing code with variables inside a loop I will not change it but if I am writing the loop I will declare them outside the loop.

Objects

For the Object test I will use the StringBuffer with the following methods:

        public void testObjects1(int count) {

                StringBuffer sb = new StringBuffer();

                for (int i = 0; i < count; i++) {

                        sb.setLength(0);

                        sb.append(i);

                }

        }



        public void testObjects2(int count) {

                StringBuffer sb;

                for (int i = 0; i < count; i++) {

                        sb = new StringBuffer();

                        sb.append(i);

                }

        }

   

        public void testObjects3(int count) {

                for (int i = 0; i < count; i++) {

                        StringBuffer sb = new StringBuffer();

                        sb.append(i);

                }

        }


Like the primitives these test methods are very simple also. For those of you who are not familiar with StringBuffer the method setLength(0) sets the length of the character sequence to 0. StringBuffer is used in these examples because it has this method which is basically a reset method so you can reuse the object.

Now on with the byte code:


public void testObjects1(int);

      Code:

       0: new #2; //class java/lang/StringBuffer

       3: dup

       4: invokespecial #3; //Method java/lang/StringBuffer."<init>":()V

       7: astore_2

       8: iconst_0

       9: istore_3

       10: iload_3

       11: iload_1

       12: if_icmpge 32

       15: aload_2

       16: iconst_0

       17: invokevirtual #4; //Method java/lang/StringBuffer.setLength:(I)V

       20: aload_2

       21: iload_3

       22: invokevirtual #5; //Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;

       25: pop

       26: iinc 3, 1

       29: goto 10

       32: return


Looking at this first method you can see the following. The first 4 lines are for declaring and initializing the StringBuffer. The next 2 are for declaring and initializing the loop variable. The next 3 are for loading the loop variable and parameter and comparing them. The next 3 are for resetting the character array. Then we load the character array and loop variable. Next the loop variable is stored to the StringBuffer and the append method is popped off the stack. Then we increment the loop variable and return to the top of the loop. In the long hand listing method as used above it looks like this:

  • Declaration and initialization of the StringBuffer.
  • Declaration and initialization of the loop variable.
  • Load the input parameter and loop variable and compare them.
  • Reset the character array in the StringBuffer.
  • Load the character array and loop variable.
  • Store the loop variable to the StringBuffer and pop the append method.
  • Increment the loop variable.
  • Goto the beginning of the loop.



Now lets look at the next example:


public void testObjects2(int);

      Code:

       0: iconst_0

       1: istore_3

       2: iload_3

       3: iload_1

       4: if_icmpge 27

       7: new #2; //class java/lang/StringBuffer

       10: dup

       11: invokespecial #3; //Method java/lang/StringBuffer."<init>:()V

       14: astore_2

       15: aload_2

       16: iload_3

       17: invokevirtual #5; //Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;

       20: pop

       21: iinc 3, 1

       24: goto 2

       27: return


This example is very similar to the last one and as in the primitives example exactly the same as the third example:


public void testObjects3(int);

      Code:

       0: iconst_0

       1: istore_2

       2: iload_2

       3: iload_1

       4: if_icmpge 27

       7: new #2; //class java/lang/StringBuffer

       10: dup

       11: invokespecial #3; //Method java/lang/StringBuffer."<init>":()V

       14: astore_3

       15: aload_3

       16: iload_2

       17: invokevirtual #5; //Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;

       20: pop

       21: iinc 2, 1

       24: goto 2

       27: return


The long hand listing of these 2 examples are as follows:

  • Declaration and initialization of the loop variable.
  • Load the input parameter and loop variable and compare them.
  • Initialization of the StringBuffer.
  • Load the character array and loop variable.
  • Store the loop variable to the StringBuffer and pop the append method.
  • Increment the loop variable.
  • Goto the beginning of the loop.



In these two examples you can see the exact same code as the first example with the exception of the order of the code and the reset method call in the first example. The big difference between the first and the other 2 is that the initialization of the StringBuffer object which creates a new object and allocates the memory for this object in the heap which is performed inside the loop. So declaring a new object inside a loop will allocate memory for the object and initializes it every time the loop is executed.

Object Conclusion

Memory and Performance

If an object is declared inside a loop the memory for the object is allocated each time and the initialization for the object is performed. The initialization may not take that much time but the memory allocation will be. In some cases you may not be able to get beyond creating a new object with every loop but if possible it is better to reset the object and reuse it.

Readability

As in the primitive example I prefer declaring my variables outside the loop and if possible resetting the object inside the loop instead of creating a new one. If the object being used is small and the loop will not execute too many times then this question may not matter as much but it will matter so be careful with this one.

Conclusion

As you can see there is a difference when declaring objects inside opposed to outside a loop though with primitives it is more of a personal choice. What I consider is that when an object can be reused inside a loop it is better to do so. So in the interest of being consistent I believe it is better to declare the variables outside the loop so when you can reuse the objects you don't have to change the way you commonly declare variables.

Related Topics >>