搜尋

首頁  >  問答  >  主體

问一个JAVA代码性能问题

1: HashMap<String, String> test = new HashMap<>();
2: Map<String, String> test = new HashMap<>();

只进行put、get操作
请问1的性能会优于2吗?为什么?

迷茫迷茫2873 天前877

全部回覆(3)我來回復

  • 伊谢尔伦

    伊谢尔伦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
    這裡有解釋

    回覆
    0
  • 阿神

    阿神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升級換代而變化的, 而且為了這種「效能提升」而帶來的程式碼改變導致可讀性、可維護性降低也是得不償失的

    回覆
    0
  • ringa_lee

    ringa_lee2017-04-17 14:47:38

    根據可操作方法論,2的表現優於1

    回覆
    0
  • 取消回覆