我经历了新的Java 8 Map方法的默认实现,getOrDefault并注意到一些有点奇怪的东西.考虑例如该getOrDefault方法.它实现如下.
default V getOrDefault(Object key, V defaultValue) {
V v;
return ((v = get(key)) != null) || containsKey(key) ? v : defaultValue;
}
Run Code Online (Sandbox Code Playgroud)
现在,这里的"怪异"的东西是"使用的分配结果"模式((v = get(key)) != null.据我所知,这种特殊的模式是不鼓励的,因为它会妨碍可读性.IMO更简洁的版本将是一个类似的东西
default V getOrDefault(Object key, V defaultValue) {
V v = get(key);
return v != null || containsKey(key) ? v : defaultValue;
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,除了编码标准/习惯之外,是否有任何特殊理由使用前者而不是后者.特别是,我想知道这两个版本是否跟踪和性能相当?
我唯一可以想象的是编译器可能会确定containsKey通常更快评估并因此首先对其进行评估,但据我所知,短路必须保持执行顺序(至少C的情况) .
编辑:在@ruakh建议之后,这里是两个字节码(由生成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
Run Code Online (Sandbox Code Playgroud)
和
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
Run Code Online (Sandbox Code Playgroud)
我不得不承认,即使经过多年的Java编码,我也不知道如何解释Java字节码.有人可以对这里的差异有所了解吗?
Rol*_*ble 10
这只是一个风格问题.有些人更喜欢最紧凑的代码,而有些人则喜欢更长但更简单的代码.似乎一些在Java核心库上工作的开发人员属于前一组.
就效率而言,两种变体都是相同的.
让我们看看编译器实际上对这两个变体做了什么:
public class ExampleMap<K, V> extends HashMap<K, V> {
V getOrDefault1(Object key, V defaultValue) {
V v;
return ((v = get(key)) != null) || containsKey(key) ? v : defaultValue;
}
V getOrDefault2(Object key, V defaultValue) {
V v = get(key);
return v != null || containsKey(key) ? v : defaultValue;
}
}
Run Code Online (Sandbox Code Playgroud)
现在让我们转储生成的字节码,使用javap -c ExampleMap:
Compiled from "ExampleMap.java"
public class ExampleMap<K, V> extends java.util.HashMap<K, V> {
public ExampleMap();
Code:
0: aload_0
1: invokespecial #1 // Method java/util/HashMap."<init>":()V
4: return
V getOrDefault1(java.lang.Object, V);
Code:
0: aload_0
1: aload_1
2: invokevirtual #2 // Method get:(Ljava/lang/Object;)Ljava/lang/Object;
5: dup
6: astore_3
7: ifnonnull 18
10: aload_0
11: aload_1
12: invokevirtual #3 // Method containsKey:(Ljava/lang/Object;)Z
15: ifeq 22
18: aload_3
19: goto 23
22: aload_2
23: areturn
V getOrDefault2(java.lang.Object, V);
Code:
0: aload_0
1: aload_1
2: invokevirtual #2 // Method get:(Ljava/lang/Object;)Ljava/lang/Object;
5: astore_3
6: aload_3
7: ifnonnull 18
10: aload_0
11: aload_1
12: invokevirtual #3 // Method containsKey:(Ljava/lang/Object;)Z
15: ifeq 22
18: aload_3
19: goto 23
22: aload_2
23: areturn
}
Run Code Online (Sandbox Code Playgroud)
如您所见,代码大多相同.唯一的小差异在于两种方法的第5和第6行.一个只是复制堆栈的顶部值(记住,Java字节码假定基于堆栈的机器模型),而另一个加载来自实例变量的(相同)值.
当Just-in-Time编译器从该字节代码生成真实的机器代码时,它将执行各种优化,例如决定将哪些值写回RAM以及将哪些值保存在CPU寄存器中.我认为可以安全地假设在这些优化发生之后,没有任何差别.
| 归档时间: |
|
| 查看次数: |
266 次 |
| 最近记录: |