首页 > 解决方案 > 为什么在比较中使用赋值?

问题描述

在阅读源代码时,我在 JDK 源代码中偶然发现了这种方法。v请注意 and 的声明和初始化newValue。我们这里有“很好”的未定义值、比较中的赋值,这是“很棒的”,以及额外的括号以降低可读性。和其他代码气味。

default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
    Objects.requireNonNull(mappingFunction);
    V v;
    if ((v = get(key)) == null) {
        V newValue;
        if ((newValue = mappingFunction.apply(key)) != null) {
            put(key, newValue);
            return newValue;
        }
    }

    return v;
}

但为什么?以上述方式而不是简单的方式编写代码是否有任何实际好处(最好使用否定v比较):

default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
    Objects.requireNonNull(mappingFunction);
    V v  = get(key);
    if (v == null) {
        V newValue = mappingFunction.apply(key);
        if (newValue != null) {
            put(key, newValue);
            return newValue;
        }
    }

    return v;
}

有什么我不知道的实际好处(除了炫耀 Java 结构),而不是采用“简单”的方式吗?

标签: java

解决方案


#microoptimization(但在标准库的情况下可能很重要),并且:

#inertia:这种模式在 90 年代的 C 程序员中很常见,因此计算机科学巨头可能仍然使用这种风格。

除非性能真的很关键,否则为新的业务逻辑编写这样的代码是没有意义的。


(微)优化:

(JDK 11)为原始(“坏”)版本生成的字节码javac比(更好的)代码少一个 JVM 操作。为什么?JDK 的版本“使用”赋值运算符的返回值(而不是从变量加载值)进行if条件评估。

然而,这更像是对javac's 优化可能性的限制,而不是编写可读性较差的代码的原因。

这是问题中引用的 JDK 版本的字节码:

   0: aload_2
   1: invokestatic  #2                  // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
   4: pop
   5: aload_0
   6: aload_1
   7: invokevirtual #3                  // Method get:(Ljava/lang/Object;)Ljava/lang/Object;
  10: dup
  11: astore_3
  12: ifnonnull     39
  15: aload_2
  16: aload_1
  17: invokeinterface #4,  2            // InterfaceMethod java/util/function/Function.apply:(Ljava/lang/Object;)Ljava/lang/Object;
  22: dup
  23: astore        4
  25: ifnull        39
  28: aload_0
  29: aload_1
  30: aload         4
  32: invokevirtual #5                  // Method put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
  35: pop
  36: aload         4
  38: areturn
  39: aload_3
  40: areturn

以下是更易读版本的字节码:

public V computeIfAbsent(K key,
                         Function<? super K, ? extends V> mappingFunction) {
    Objects.requireNonNull(mappingFunction);
    final V v = get(key);
    if (v == null) {
        final V newValue = mappingFunction.apply(key);
        if (newValue != null) {
            put(key, newValue);
            return newValue;
        }
    }

    return v;
}

..字节码是:

   0: aload_2
   1: invokestatic  #2                  // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
   4: pop
   5: aload_0
   6: aload_1
   7: invokevirtual #3                  // Method get:(Ljava/lang/Object;)Ljava/lang/Object;
  10: astore_3
  11: aload_3
  12: ifnonnull     40
  15: aload_2
  16: aload_1
  17: invokeinterface #4,  2            // InterfaceMethod java/util/function/Function.apply:(Ljava/lang/Object;)Ljava/lang/Object;
  22: astore        4
  24: aload         4
  26: ifnull        40
  29: aload_0
  30: aload_1
  31: aload         4
  33: invokevirtual #5                  // Method put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
  36: pop
  37: aload         4
  39: areturn
  40: aload_3
  41: areturn

推荐阅读