Which of the following Java code snippets is better?

This does not mean to be subjective, I am looking for reasons based on the use of resources, compiler performance, GC performance, etc., and not on elegance. Oh, and the position of the brackets is not taken into account, so no stylistic comments are needed.

Take the following loop:

Integer total = new Integer(0); Integer i; for (String str : string_list) { i = Integer.parse(str); total += i; } 

compared with...

 Integer total = 0; for (String str : string_list) { Integer i = Integer.parse(str); total += i; } 

In the first, I am the scope, and in the second, in the loop. I always thought (believed) that the first one would be more efficient, because it just refers to an existing variable already allocated on the stack, while the second one will push and pop out each iteration of the loop.

There are quite a few other cases where I am inclined towards coverage variables more broadly than possible, so I thought I would ask here to clarify the gap in my knowledge. Also note that the assignment of a variable during initialization is related to the new operator or not. Does any of these semi-stylistic semi-optimizations not matter?

+4
source share
10 answers

The second is what I would prefer. There is no functional difference, except for the field of view.

Setting the same variable in each iteration does not matter, because Integer is an immutable class. Now, if you changed an object each time, and not each time created a new one, then there would be a difference.

And as a note, in this code you should use int and Integer.parseInt() , not Integer and Integer.parse() . You introduce quite a bit of unnecessary boxing and unboxing.


Edit: It has been a while since I retired to the bytecode, so I thought I'd hold my hands again.

Here's the test class I compiled:

 class ScopeTest { public void outside(String[] args) { Integer total = 0; Integer i; for (String str : args) { i = Integer.valueOf(str); total += i; } } public void inside(String[] args) { Integer total = 0; for (String str : args) { Integer i = Integer.valueOf(str); total += i; } } } 

Bytecode output (obtained after javap -c ScopeTest after compilation):

 Compiled from "ScopeTest.java" class ScopeTest extends java.lang.Object{ ScopeTest(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return public void outside(java.lang.String[]); Code: 0: iconst_0 1: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 4: astore_2 5: aload_1 6: astore 4 8: aload 4 10: arraylength 11: istore 5 13: iconst_0 14: istore 6 16: iload 6 18: iload 5 20: if_icmpge 55 23: aload 4 25: iload 6 27: aaload 28: astore 7 30: aload 7 32: invokestatic #3; //Method java/lang/Integer.valueOf:(Ljava/lang/String;)Ljava/lang/Integer; 35: astore_3 36: aload_2 37: invokevirtual #4; //Method java/lang/Integer.intValue:()I 40: aload_3 41: invokevirtual #4; //Method java/lang/Integer.intValue:()I 44: iadd 45: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 48: astore_2 49: iinc 6, 1 52: goto 16 55: return public void inside(java.lang.String[]); Code: 0: iconst_0 1: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 4: astore_2 5: aload_1 6: astore_3 7: aload_3 8: arraylength 9: istore 4 11: iconst_0 12: istore 5 14: iload 5 16: iload 4 18: if_icmpge 54 21: aload_3 22: iload 5 24: aaload 25: astore 6 27: aload 6 29: invokestatic #3; //Method java/lang/Integer.valueOf:(Ljava/lang/String;)Ljava/lang/Integer; 32: astore 7 34: aload_2 35: invokevirtual #4; //Method java/lang/Integer.intValue:()I 38: aload 7 40: invokevirtual #4; //Method java/lang/Integer.intValue:()I 43: iadd 44: invokestatic #2; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 47: astore_2 48: iinc 5, 1 51: goto 14 54: return } 

Unlike my expectations, there was one difference between them: in outside() variable i still occupied the register, even if it was excluded from the actual code (note that all iload and istore instructions indicate the same register above).

The JIT compiler should do a short job with this difference, but still you can see that the bounding area is good practice.

(As for my previous post, you can see that to add two Integer objects, Java must unzip both with intValue , add them, and then create a new Integer with valueOf . This, if absolutely necessary, because it makes no sense and is slower.)

+7
source

The second option is much better, because the first style should only be used in C code as a must. Java allows inline declarations to minimize the scope of variables, and you should take advantage of this. But the code can be improved:

 int total = 0; for (String str: stringList) { try { total += Integer.valueOf(str); } catch(NumberFormationException nfe) { // more code to deal with the error } } 

This follows the Java code style convention. Read the full guide here: http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html

+4
source

This has no significant differences, except for the last iteration, when the link will be deleted faster in the second example (and this would be my preference - not so much for this reason, but with clarity.)

Keep the area to a minimum. The virtual access point performs a transition analysis to determine when links are no longer available, and based on this it allocates some objects on the stack rather than on the heap. Keeping the opportunity as small as possible helps this process.

I would ask why you use Integer instead of a simple int ... or maybe just as an example?

0
source

The second is much better. Moving variables are as narrow as possible, making the code much easier to read and maintain, which is much more important than the performance differences between these examples, which are trivial and easily optimized.

0
source

None. Integer.valueOf(0); will use the link to cached 0. :)

0
source

Well, in this case, you create an Integer primitive every time you say i = Integer.parseInt(str) (where I am Integer ), so (if Java does not know how to optimize it), both cases are almost equally inefficient. Use int instead:

 int total = 0; for (String str : string_list) { int i = Integer.parseInt(str); total += i; } 

Now we come back to the question of whether to introduce an int declaration inside or outside. Assuming the Java compiler has a decent optimization lick, I would say that it doesn't matter. Efficiency aside, it is considered good practice to declare variables as close as possible to their use.

0
source

The second option is preferable to two for readability, maintainability and effectiveness.

All three of these goals are achieved because you briefly explain what you are doing and how your variables are used. You clearly explain this to both the developers and the compiler. When the variable i defined in the for block, everyone knows that it is safe to ignore it outside the block and that this value is valid only for this iteration of the block. This will cause the garbage collector to easily tag this memory for free.

I would suggest not using Integer for intermediate values. Accumulate the total as int even after the loop creates an object or depends on an automatic box.

0
source

Assuming you have positive numbers on your list and you are serious about

I am looking for resource based reasons, compiler performance, GC performance, etc. not elegance.

You must implement this yourself, as:

 import java.util.ArrayList; import java.util.List; public class Int { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("10"); list.add("20"); int total = 0; for (String str : list) { int val = 0; for (char c : str.toCharArray()) { val = val * 10 + (int) c - 48; } total += val; } System.out.print(total); } } 

The only suitable problem for GC would be toCharArray() , which can be replaced with another loop using charAt()

0
source

The question of which scope of variables to use is a readability problem more than anything else. The code is better understood when each variable is limited to the area in which it is actually used.

Now, if we look at the technical implications of using widescreen / narrow areas, I believe that the advantage is in performance / features with narrow areas. Consider the following method, where we have 3 local variables that belong to the same global region:

 private static Random rnd = new Random(); public static void test() { int x = rnd.nextInt(); System.out.println(x); int y = rnd.nextInt(); System.out.println(y); int z = rnd.nextInt(); System.out.println(z); } 

If you combine this code (for example, using javap -c -verbose {class name}), you will see that the compiler reserves 3 slots for local variables in the frame structure of the test stack ().

Now suppose we add some artificial areas:

 public static void test() { { int x = rnd.nextInt(); System.out.println(x); } { int y = rnd.nextInt(); System.out.println(y); } { int z = rnd.nextInt(); System.out.println(z); } } 

If you break the code now, you will notice that the compiler reserves only 1 slot for local variables. Since the areas are completely independent, the same slot # 0 is used every time x, y or z is used.

What does it mean?

1) Narrow areas of stack space preservation

2) If we are dealing with object variables, this means that objects can become inaccessible faster, so they are more likely to work for GC sooner than otherwise.

Again, note that these 2 “benefits” are actually negligible, and concern for readability should by far be the most important issue.

0
source

The second, because you want the scope of your variables to be as "internal" as possible. The advantage of a smaller volume is less likely to collide. There are only a few lines in your example, so the advantage may not be so obvious. But if it is larger, then having smaller variables is definitely more profitable. If someone else has to look at the code, they will have to scan all the way back outside the method definition to find out what i is . The argument is not much different from the argument why we want to avoid a global variable.

-1
source

Source: https://habr.com/ru/post/1310564/


All Articles