簡體   English   中英

單線程應用程序中的哈希表與 HashMap 性能

[英]Hashtable vs HashMap performance in single threaded app

我知道Hashtable是同步的,因此在多線程應用程序中使用是安全的,而HashMap不是。

我想知道這兩者在單線程應用程序中是否存在任何性能差異。

(或者,什么時候使用一個而不是另一個?)

如果你想要一個線程安全的集合,你可以使用 ConcurrentHashMap 或 Collections.synchronizedMap() 和 LinkedHashMap 或 HashMap。 如果您不需要線程安全集合,您可以只使用最后兩個。 Hashtable 已經過改造以支持 Map 和 generics 但它也帶有許多做同樣事情或幾乎同樣事情的傳統方法。

可以使用哈希表,但是恕我直言,使用后來開發的許多其他選項之一將是一個更清潔的解決方案。 如果您有一個需要 Hashtable 的庫,那么您需要使用它,否則我會使用 class 來滿足您的需求,遵循最佳實踐,使用最少的遺留方法。

每次調用的性能差異可能約為 0.5 us。 這可能很重要,也可能不重要。

但是,如果您不需要類型是線程安全的,則沒有充分的理由使用同步版本。 如果你需要一個類型是線程安全的,你就不能使用一個沒有一些線程安全防護的類型。

接受@svick 在評論中提到的內容。 If you are talking about the Hashtable and HashMap included with the Java SDK, there is definitely a performance difference, as HashMap doesn't have to use the synchronized blocks, which have an overhead.

根據 pst 的要求,這里有一些關於同步性能的閱讀,這里有一些更新的內容,關於 Java 1.4 與 Java 6在一台機器上。

差異

  • HashMap 允許 null 值,哈希表不允許。
  • 哈希表是同步的,HashMap 不是。 (但如果不修改它仍然可以用於多線程讀取,fe - 在啟動時初始化一次,並且僅針對某些 static 緩存從中讀取)

表現

這里有一些單線程測試來比較它們。 100 次磨機操作的 5 次嘗試(第一次嘗試可能被認為是熱身)put 是 100% 的碰撞,get 是 50% 的命中。

1 HashMap put/get -->   419.80  /   354.09  ms
2 HashMap put/get -->   983.02  /   305.54  ms
3 HashMap put/get -->   976.26  /   358.72  ms
4 HashMap put/get -->   989.04  /   375.18  ms
5 HashMap put/get -->   974.13  /   360.73  ms

1 Hashtable put/get -->   776.97  /   708.39  ms
2 Hashtable put/get -->   776.26  /   736.23  ms
3 Hashtable put/get -->   794.01  /   740.07  ms
4 Hashtable put/get -->   784.23  /   734.40  ms
5 Hashtable put/get -->   782.45  /   729.48  ms

1 Synced-HashMap put/get -->  1523.61  /  1215.63  ms
2 Synced-HashMap put/get -->  1491.59  /  1090.83  ms
3 Synced-HashMap put/get -->  1442.67  /  1095.62  ms
4 Synced-HashMap put/get -->  1439.19  /  1082.57  ms
5 Synced-HashMap put/get -->  1450.04  /  1101.53  ms
  • 單線程
    HashMap 通常更快。 它的獲取速度比 Hashtable 快 2 倍 但是,它的看跌期權慢了 25%
  • 並行使用
    如果不需要 null 值,則為哈希表,如果需要空值,則為 Collections.synchronizedMap(new HashMap<>())。 請注意,Synchronized-HashMap 比 Hashtable 慢(慢 2 倍,慢 50%)

用於測試的代碼為 JUnit:

import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import org.junit.Test;

public class MapsPerfTest {

    @Test
    public void testMaps() {
        testMap("HashMap", new HashMap<>());
        testMap("Hashtable", new Hashtable<>());
        testMap("Synced-HashMap", Collections.synchronizedMap(new HashMap<>()));
    }

    void testMap(String name, Map<Integer, String> h) {
        for(int i=1; i<=tries; ++i) {
            long t1 = timeit(() -> testMapPut(h));
            long t2 = timeit(() -> testMapGet(h));
            System.out.println(String.format("%d %s put/get -->  %7.2f  /  %7.2f  ms",
                    i, name, t1/1000/1000.0, t2/1000/1000.0));
        }
    }

    long timeit(Runnable r) {
        System.gc();
        long t = System.nanoTime();
        r.run();
        return System.nanoTime() - t;
    }

    static final int tries = 5;
    static final int count = 100000000;

    static final String VALUE = "-";

    static final int putSpace = 100;
    static final int getSpace = putSpace*2;
    static final Integer[] numbers = new Integer[getSpace+1];

    static {
        for(int i=getSpace; i>=0; --i)
            numbers[i] = i;
    }

    void testMapPut(Map<Integer, String> m) {
        for(int i=count; i>0; --i)
            m.put(numbers[i%putSpace], VALUE);
    }

    void testMapGet(Map<Integer, String> m) {
        for(int i=count; i>0; --i)
            m.get(numbers[i%getSpace]);
    }
}

由於 Java 7 聲稱它可以進行逃逸分析,並在某些情況下刪除無競爭同步,因此我對其進行了測試

public static void main(String[] args)
{
    for(int i=0; i<100; i++)
    {
        System.out.println("-------------------");
        testS();
    }
}
static int N = 100_000_000;
static void testS()
{
    Object o = new Object();
    long t0 = System.nanoTime();
    for(int i=0; i<N; i++)
        synchronized (o){}
    long t = System.nanoTime() - t0;
    System.out.printf("time: %,d%n", t);
}

我想不出一個更簡單的逃逸分析示例。 但是,顯然 Java 7 在我的測試中沒有優化同步關閉; 每個synchronized (o){}都會消耗一些時間。

令人驚訝的是,它只消耗大約 1 個 CPU 周期,這太快了,令人難以置信。 它應該包含至少兩個比較和設置指令; 訪問 L1 緩存通常需要 10 個周期。 顯然有一些硬件優化開始了。

這是一個緊密的循環,而不是真正的應用程序。 籠統地討論真正的應用程序太難了; 即使是具體的應用也很難分析。 那么如果可能的話,我們可能應該更喜歡 HashMap,據我們所知,它至少不會比 Hashtable 慢。

是的。 這是 HashMap 默認情況下未同步的(其中一個)要點(使用 synchronizedMap() 使其同步;盡管請注意,根據您的使用情況,僅簡單的同步可能不足以保持您可能想要的所有操作的完整性做)。

如果您甚至可以測量真實世界測試中的差異,我會感到驚訝。 如果你測量數以億計的操作,也許,但你不會做數以億計,你將很難達到甚至第一個百萬。

如果在 Hashtable 與 HashMap 上沒有數字,但幾年前我將 Vector 與 ArrayList 進行了比較,其中存在類似的問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM