猿问

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

在阅读源代码时,我在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 构造),而不是采用“简单”的方式?

凤凰求蛊
浏览 148回答 3
3回答

料青山看我应如是

microoptimization(但在标准库的情况下可能很重要),以及:#inertia:这种模式在 90 年代的 C 程序员中很常见,所以计算机科学的巨头可能仍然使用这种风格。除非性能真的很关键,否则为新的业务逻辑编写这样的代码是没有意义的。(微)优化:javac(JDK 11) 为原始(“坏”)版本生成的字节码比(更好的)代码少一个 JVM 操作。为什么?JDK 的版本“使用”赋值运算符的返回值(而不是从变量加载值)进行if条件评估。然而,这更多是对javac优化可能性的限制,而不是编写可读性较差的代码的原因。这是问题中引用的 JDK 版本的字节码:&nbsp; &nbsp;0: aload_2&nbsp; &nbsp;1: invokestatic&nbsp; #2&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;&nbsp; &nbsp;4: pop&nbsp; &nbsp;5: aload_0&nbsp; &nbsp;6: aload_1&nbsp; &nbsp;7: invokevirtual #3&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method get:(Ljava/lang/Object;)Ljava/lang/Object;&nbsp; 10: dup&nbsp; 11: astore_3&nbsp; 12: ifnonnull&nbsp; &nbsp; &nbsp;39&nbsp; 15: aload_2&nbsp; 16: aload_1&nbsp; 17: invokeinterface #4,&nbsp; 2&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // InterfaceMethod java/util/function/Function.apply:(Ljava/lang/Object;)Ljava/lang/Object;&nbsp; 22: dup&nbsp; 23: astore&nbsp; &nbsp; &nbsp; &nbsp; 4&nbsp; 25: ifnull&nbsp; &nbsp; &nbsp; &nbsp; 39&nbsp; 28: aload_0&nbsp; 29: aload_1&nbsp; 30: aload&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;4&nbsp; 32: invokevirtual #5&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;&nbsp; 35: pop&nbsp; 36: aload&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;4&nbsp; 38: areturn&nbsp; 39: aload_3&nbsp; 40: areturn以下是更易读版本的字节码:public V computeIfAbsent(K key,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Function<? super K, ? extends V> mappingFunction) {&nbsp; &nbsp; Objects.requireNonNull(mappingFunction);&nbsp; &nbsp; final V v = get(key);&nbsp; &nbsp; if (v == null) {&nbsp; &nbsp; &nbsp; &nbsp; final V newValue = mappingFunction.apply(key);&nbsp; &nbsp; &nbsp; &nbsp; if (newValue != null) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; put(key, newValue);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return newValue;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; return v;}.. 字节码是:&nbsp; &nbsp;0: aload_2&nbsp; &nbsp;1: invokestatic&nbsp; #2&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;&nbsp; &nbsp;4: pop&nbsp; &nbsp;5: aload_0&nbsp; &nbsp;6: aload_1&nbsp; &nbsp;7: invokevirtual #3&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method get:(Ljava/lang/Object;)Ljava/lang/Object;&nbsp; 10: astore_3&nbsp; 11: aload_3&nbsp; 12: ifnonnull&nbsp; &nbsp; &nbsp;40&nbsp; 15: aload_2&nbsp; 16: aload_1&nbsp; 17: invokeinterface #4,&nbsp; 2&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // InterfaceMethod java/util/function/Function.apply:(Ljava/lang/Object;)Ljava/lang/Object;&nbsp; 22: astore&nbsp; &nbsp; &nbsp; &nbsp; 4&nbsp; 24: aload&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;4&nbsp; 26: ifnull&nbsp; &nbsp; &nbsp; &nbsp; 40&nbsp; 29: aload_0&nbsp; 30: aload_1&nbsp; 31: aload&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;4&nbsp; 33: invokevirtual #5&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // Method put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;&nbsp; 36: pop&nbsp; 37: aload&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;4&nbsp; 39: areturn&nbsp; 40: aload_3&nbsp; 41: areturn

繁星coding

我认为这主要是偏好问题;我的印象是 Doug Lea 更喜欢这种简洁的风格。但是如果您查看该方法的原始版本,它会更有意义一些,因为它与newValuewhich 确实需要内联分配配对:default V computeIfAbsent(K key,&nbsp; &nbsp; &nbsp; &nbsp; Function<? super K, ? extends V> mappingFunction) {&nbsp; &nbsp; V v, newValue;&nbsp; &nbsp; return ((v = get(key)) == null &&&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (newValue = mappingFunction.apply(key)) != null &&&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (v = putIfAbsent(key, newValue)) == null) ? newValue : v;}

猛跑小猪

我同意:这是一种代码味道,我绝对更喜欢第二个版本。如果您有循环,那么在单个表达式中对赋值和比较的神秘使用可能会导致代码更短,例如:V v;while ((v = getNext()) != null) {&nbsp; &nbsp; // ... do something with v ...}要消除代码异味,您需要更多代码,并且需要在两个地方分配变量:V v = getNext();while (v != null) {&nbsp; &nbsp; // ...&nbsp; &nbsp; v = getNext();}或者你需要在赋值后移动循环退出条件:while (true) {&nbsp; &nbsp; V v = getNext();&nbsp; &nbsp; if (v == null)&nbsp; &nbsp; &nbsp; &nbsp; break;&nbsp; &nbsp; // ...}对于if语句,它当然没有任何意义。即使是循环,我也会避免它。
随时随地看视频慕课网APP

相关分类

Java
我要回答