1: HashMap<String, String> test = new HashMap<>();
2: Map<String, String> test = new HashMap<>();
只进行put、get操作
请问1的性能会优于2吗?为什么?
伊谢尔伦2017-04-17 14:47:38
HashMap<String, String> map1 = new HashMap<>();
Map<String, String> map2 = new HashMap<>();
map1.put("a", "b");
map2.put("a", "b");
=>
16 aload_1 [map1]
17 ldc <String "a"> [160]
19 ldc <String "b"> [162]
21 invokevirtual java.util.HashMap.put(java.lang.Object, java.lang.Object) : java.lang.Object [164]
24 pop
25 aload_2 [map2]
26 ldc <String "a"> [160]
28 ldc <String "b"> [162]
30 invokeinterface java.util.Map.put(java.lang.Object, java.lang.Object) : java.lang.Object [168] [nargs: 3]
这里的测试中, http://bobah.net/book/export/html/55
invokeinterface可能慢38%
http://stackoverflow.com/questions/1504633/what-is-the-point-of-invokeinterface
这里有解释
阿神2017-04-17 14:47:38
对于这个问题,一般的答案是“差不多,没有区别”;
而钻牛角尖的答案是“2的性能比1稍好”;
下面的代码:
HashMap<String, String> m1 = new HashMap<>();
m1.put("test", "test");
m1.get("test");
Map<String, String> m2 = new HashMap<>();
m2.put("test", "test");
m2.get("test");
编译成字节码后对应的指令是:
0: new #16 // class java/util/HashMap
3: dup
4: invokespecial #18 // Method java/util/HashMap."<init>":()V
7: astore_1
8: aload_1
9: ldc #19 // String test
11: ldc #19 // String test
13: invokevirtual #21 // Method java/util/HashMap.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
16: pop
17: aload_1
18: ldc #19 // String test
20: invokevirtual #25 // Method java/util/HashMap.get:(Ljava/lang/Object;)Ljava/lang/Object;
23: pop
24: new #16 // class java/util/HashMap
27: dup
28: invokespecial #18 // Method java/util/HashMap."<init>":()V
31: astore_2
32: aload_2
33: ldc #19 // String test
35: ldc #19 // String test
37: invokeinterface #29, 3 // InterfaceMethod java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
42: pop
43: aload_2
44: ldc #19 // String test
46: invokeinterface #32, 2 // InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;
51: pop
52: return
可见情况1的map的put/get操作是用invokevirtual
指令完成的;
而情况2的map的put/get操作是用invokeinterface
指令完成的;
而论实现的话,invokevirtual
的性能略优于invokeinterface
, 因此硬要说谁性能好的话那就是2;
最后提醒一下,在java编程过程中,任何jvm指令我们都应该看作是差不多一样的常数级时间开销,哪怕它是invokedynamic
,这才能为我们的上层算法、逻辑优化带来统一、无干扰的视角;
为了钻牛角尖地挑选jvm指令而改变java代码的写法是不理智的,且其结论也是不稳定的 —— 它可能会随着jvm升级换代而变化的, 而且为了这种“性能提升”而带来的代码改动导致可读性、可维护性降低也是得不偿失的