Java 8 Map default implementation details

Viewed 423

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?

3 Answers
Related