Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why not use values variable directly, the vs variable is not necessary

Tags:

java

Here is from HashMap:

transient Collection<V> values;

public Collection<V> values() {
    Collection<V> vs = values;
    if (vs == null) {
        vs = new Values();
        values = vs;
    }
    return vs;
}

I wonder why not use member variable values directly? Why create the local variable named vs?

How is that better than:

transient Collection<V> values;

public Collection<V> values() {
    if (values == null) {
        values = new Values();
    }
    return values;
}
like image 587
Raw Avatar asked Dec 12 '19 08:12

Raw


People also ask

Why should you try and avoid using unnecessary variables?

Avoid Declaring Unnecessary Variables The simple reason why we don't want extra variables is because they can make our code difficult to read and maintain.

Is it necessary to declare a variable before it is used?

Variables and data structures must be defined and initialized before they can be used to store data.

Why are variables necessary in programming?

Variables are used to store information to be referenced and manipulated in a computer program. They also provide a way of labeling data with a descriptive name, so our programs can be understood more clearly by the reader and ourselves.

Which of the following elements is not required in variable declaration?

The element “ an assigned value ” is not required in variable declaration. Hence, the correct answer is option “ C ”.


1 Answers

Local variables are held on the stack and are very fast, faster than reading a field, even before the JVM optimizes the method (if it bothers to, the method may not be called enough in a given program). In the common case, values is not null, so it's retrieved once, then the local is tested and used as the return value. If you didn't use a local variable, you'd have to read the field twice (once for the null check, once for the return). That isn't as fast as reusing the local.

There may also be a general style rule (related to efficiency) being applied that doesn't necessarily show its benefits in values(), specifically.


Let's look at it in more depth. Consider these two implementations of a values-like method, one using a local and the other using the field everywhere:

private transient Map<T, U> map1 = null;
public Map<T, U> likeValues() {
    Map<T, U> m = this.map1;
    if (m == null) {
        m = new HashMap<T, U>();
        this.map1 = m;
    }
    return m;
}

private transient Map<T, U> map2 = null;
public Map<T, U> usingField() {
    if (this.map2 == null) {
        this.map2 = new HashMap<T, U>();
    }
    return this.map2;
}

Let's look at the bytecode for them, I've added comments (they may not be perfect, I'm not a bytecode expert and moreover I'm going for clarity rather than describing each and every stack operation in detail):

public java.util.Map likeValues();
  Code:
     0: aload_0             // Load `this` onto the stack
     1: getfield      #2    // Get the field's value
     4: astore_1            // Store it in local variable 1
     5: aload_1             // Load local variable 1
     6: ifnonnull     22    // Jump if not null to #22
     9: new           #5    // Create the HashMap
    12: dup                 // Duplicate the top value on the stack
    13: invokespecial #6    // Call method java/util/HashMap."":()V to init the HashMap
    16: astore_1            // Store the result in local variable 1
    17: aload_0             // Load `this` onto the stack
    18: aload_1             // Load local variable 1 onto the stack
    19: putfield      #2    // Store the value in the field
    22: aload_1             // Load local variable 1 onto the stack
    23: areturn             // Return it

public java.util.Map usingField();
  Code:
     0: aload_0             // Load `this` onto the stack
     1: getfield      #3    // Get the field's value
     4: ifnonnull     18    // Jump if not null to #18
     7: aload_0             // Load `this` onto the stack
     8: new           #5    // Create the HashMap
    11: dup                 // Duplicate the top value on the stack                          
    12: invokespecial #6    // Call method java/util/HashMap."":()V to init the HashMap
    15: putfield      #3    // Store it in the field
    18: aload_0             // Load `this` onto the stack
    19: getfield      #3    // Get the field's value
    22: areturn             // Return it

Let's look only at the non-null path in those:

public java.util.Map likeValues();
  Code:
     0: aload_0             // Load `this` onto the stack
     1: getfield      #2    // Get the field's value
     4: astore_1            // Store it in local variable 1
     5: aload_1             // Load local variable 1
     6: ifnonnull     22    // Jump if not null to #22
    // ...skipped...
    22: aload_1             // Load local variable 1 onto the stack
    23: areturn             // Return it

public java.util.Map usingField();
  Code:
     0: aload_0             // Load `this` onto the stack
     1: getfield      #3    // Get the field's value
     4: ifnonnull     18    // Jump if not null to #18
    // ...skipped...
    18: aload_0             // Load `this` onto the stack
    19: getfield      #3    // Get the field's value
    22: areturn             // Return it

In likeValues, the field is only read once, and then the local variable (on the stack) is used for the comparison and return.

In usingField, the field is read twice. Reading a field is not as fast as using a stack value.

like image 75
T.J. Crowder Avatar answered Oct 15 '22 11:10

T.J. Crowder