为了账号安全,请及时绑定邮箱和手机立即绑定

为什么在比较中使用赋值?

为什么在比较中使用赋值?

凤凰求蛊 2021-12-10 14:46:33
在阅读源代码时,我在JDK源中偶然发现了这种方法。请注意,声明和初始化v和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 构造),而不是采用“简单”的方式?
查看完整描述

3 回答

?
料青山看我应如是

TA贡献1772条经验 获得超8个赞

microoptimization(但在标准库的情况下可能很重要),以及:


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


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


(微)优化:

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


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


这是问题中引用的 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


查看完整回答
反对 回复 2021-12-10
?
繁星coding

TA贡献1797条经验 获得超4个赞

我认为这主要是偏好问题;我的印象是 Doug Lea 更喜欢这种简洁的风格。但是如果您查看该方法的原始版本,它会更有意义一些,因为它与newValuewhich 确实需要内联分配配对:


default V computeIfAbsent(K key,

        Function<? super K, ? extends V> mappingFunction) {

    V v, newValue;

    return ((v = get(key)) == null &&

            (newValue = mappingFunction.apply(key)) != null &&

            (v = putIfAbsent(key, newValue)) == null) ? newValue : v;

}


查看完整回答
反对 回复 2021-12-10
?
猛跑小猪

TA贡献1858条经验 获得超8个赞

我同意:这是一种代码味道,我绝对更喜欢第二个版本。


如果您有循环,那么在单个表达式中对赋值和比较的神秘使用可能会导致代码更短,例如:


V v;

while ((v = getNext()) != null) {

    // ... do something with v ...

}

要消除代码异味,您需要更多代码,并且需要在两个地方分配变量:


V v = getNext();

while (v != null) {

    // ...

    v = getNext();

}

或者你需要在赋值后移动循环退出条件:


while (true) {

    V v = getNext();

    if (v == null)

        break;

    // ...

}

对于if语句,它当然没有任何意义。即使是循环,我也会避免它。


查看完整回答
反对 回复 2021-12-10
  • 3 回答
  • 0 关注
  • 147 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信