I went through the default implementation of the new Java 8 Map methods like getOrDefault and noticed something slightly weird. Consider for example the getOrDefault method. It is implemented as follows.
default V getOrDefault(Object key, V defaultValue) {
V v;
return ((v = get(key)) != null) || containsKey(key) ? v : defaultValue;
}
Now, the "weird" thing here is the "Result of assignment used" pattern in ((v = get(key)) != null. To my knowledge, this particular pattern is discouraged, since it rather obstructs readability. An IMO more concise version would be something along the lines of
default V getOrDefault(Object key, V defaultValue) {
V v = get(key);
return v != null || containsKey(key) ? v : defaultValue;
}
My question is if there is any particular reason to use the former over the latter pattern aside from coding standards / habits. In particular, I wonder if these two versions are trace and performance equivalent?
The only thing I could imagine is that the compiler might e.g. determine that containsKey is usually faster to evaluate and thus evaluates it first, but as far as I know short-circuiting has to preserve order of execution (this is the case for C at least).
EDIT: Following @ruakh suggestion, here are the two bytecodes (as generated by javap -c)
public V getOrDefault(java.lang.Object, V);
Code:
0: aload_0
1: aload_1
2: invokeinterface #1, 2 // InterfaceMethod get:(Ljava/lang/Object;)Ljava/lang/Object;
7: dup // <-- difference here
8: astore_3
9: ifnonnull 22
12: aload_0
13: aload_1
14: invokeinterface #2, 2 // InterfaceMethod containsKey:(Ljava/lang/Object;)Z
19: ifeq 26
22: aload_3
23: goto 27
26: aload_2
27: areturn
and
public V getOrDefault(java.lang.Object, V);
Code:
0: aload_0
1: aload_1
2: invokeinterface #1, 2 // InterfaceMethod get:(Ljava/lang/Object;)Ljava/lang/Object;
7: astore_3
8: aload_3 // <-- difference here
9: ifnonnull 22
12: aload_0
13: aload_1
14: invokeinterface #2, 2 // InterfaceMethod containsKey:(Ljava/lang/Object;)Z
19: ifeq 26
22: aload_3
23: goto 27
26: aload_2
27: areturn
I have to admit that even after years and years of Java coding I have no idea how to interpret Java bytecode. Could someone kindly shed some light on the difference here?