[英]Hashtable vs HashMap performance in single threaded app
如果你想要一個線程安全的集合,你可以使用 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在一台機器上。
這里有一些單線程測試來比較它們。 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
用於測試的代碼為 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.