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;
}
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.
Variables and data structures must be defined and initialized before they can be used to store data.
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.
The element “ an assigned value ” is not required in variable declaration. Hence, the correct answer is option “ C ”.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With