java - 为什么在比较中使用赋值?
问题描述
在阅读源代码时,我在 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 结构),而不是采用“简单”的方式吗?
解决方案
#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
推荐阅读
- python - 用python生成关联规则后计算总价和数量
- php - 如何使用 php 在循环中制作完美的表?
- .net - Automapper 10 和 Simple Injector v5 设置“SimpleInjector.ActivationException”
- c# - 通过数据库查询并在cshtml上显示
- javascript - 使用 Google Chrome DevTools 点击页面上的“Follow”按钮
- visual-studio-code - VS 代码终端不能用作 cmd
- dynamics-365 - 使用 DMF 禁用 D365 FinOps 中的所有用户
- r - 创建用于生成特定位置图的“包装器”函数
- node.js - NodeJs + TS 不识别任何路由
- java - 为什么邮递员显示404未找到