1: HashMap<String, String> test = new HashMap<>();
2: Map<String, String> test = new HashMap<>();
只进行put、get操作
请问1的性能会优于2吗?为什么?
1: HashMap<String, String> test = new HashMap<>();
2: Map<String, String> test = new HashMap<>();
只进行put、get操作
请问1的性能会优于2吗?为什么?
对于这个问题,一般的答案是“差不多,没有区别”;
而钻牛角尖的答案是“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升级换代而变化的, 而且为了这种“性能提升”而带来的代码改动导致可读性、可维护性降低也是得不偿失的
通常来说,我们应该优先用interface:
Map<String, String> test = new HashMap<>();
// is better than
HashMap<String, String> test = new HashMap<>();
请参阅:https://stackoverflow.com/que...
关于性能,首先我们要明白写一个正确的性能测试并不是件容易的事,如果确实有性能的担忧,最好用实际的用例来测试;
public class InvokevirtualVsInvokeinterface {
private static interface I {
public int getInteger();
}
private static class A implements I {
@Override
public int getInteger() {
return 0;
}
}
static volatile I i = new A();
static volatile A a = new A();
public static void main(String[] args) {
{
long tm1 = System.nanoTime();
for (int k = 0; k < 100000000; ++k) {
i.getInteger();
}
long tm2 = System.nanoTime();
System.out.println("invokeinterface took " + (Math.abs(tm2 - tm1) / 1000) + " us");
}
{
long tm1 = System.nanoTime();
for (int k = 0; k < 100000000; ++k) {
a.getInteger();
}
long tm2 = System.nanoTime();
System.out.println("invokevirtual took " + (Math.abs(tm2 - tm1) / 1000) + " us");
}
{
Map<String, Integer> map = new HashMap<>();
long tm1 = System.nanoTime();
for (int k = 0; k < 100000000; ++k) {
map.put("a", 1);
}
long tm2 = System.nanoTime();
System.out.println("invokeinterface-Map took " + (Math.abs(tm2 - tm1) / 1000) + " us");
}
{
HashMap<String, Integer> map = new HashMap<>();
long tm1 = System.nanoTime();
for (int k = 0; k < 100000000; ++k) {
map.put("a", 1);
}
long tm2 = System.nanoTime();
System.out.println("invokevirtual-HashMap took " + (Math.abs(tm2 - tm1) / 1000) + " us");
}
}
}
通过运行上面的代码,在我的机子上我得到两个关于invokeinterface和invokevirtual绝然不同的结论:
通过比较前面两个测试结果,得到invokevirtual比invokeinterface快的结论
通过比较后面两个测试结果,得到invokevirtual比invokeinterface慢的结论
而真正的原因是上面所有的测试本身就是不正确,通过比较它们的测试结果得到的结论自然也就不正确。
4 回答1.4k 阅读✓ 已解决
4 回答1.2k 阅读✓ 已解决
1 回答2.6k 阅读✓ 已解决
2 回答737 阅读✓ 已解决
2 回答1.7k 阅读
2 回答1.7k 阅读
2 回答1.3k 阅读
=>
这里的测试中, http://bobah.net/book/export/html/55
invokeinterface可能慢38%
http://stackoverflow.com/questions/1504633/what-is-the-point-of-invokeinterface
这里有解释