单线程应用程序中的 Hashtable 与 HashMap 性能

cae*_*kim 4 java data-structures

我知道Hashtable是同步的,所以在多线程应用程序中使用它是安全的,而HashMap不是。

我想知道在单线程应用程序中这两者之间是否有任何性能差异。

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

Pet*_*rey 5

如果你想要一个线程安全的集合,你可以将 ConcurrentHashMap 或 Collections.synchronizedMap() 与 LinkedHashMap 或 HashMap 一起使用。如果您不需要线程安全集合,您可以只使用最后两个。Hashtable 已经被改造为支持带有泛型的 Map,但它也带有许多传统方法,它们可以做同样的事情或几乎相同的事情。

可以使用哈希表,但是恕我直言,使用后来开发的许多其他选项之一将是一个更清洁的解决方案。如果您有一个需要 Hashtable 的库,那么您需要使用它,否则我将使用一个可以满足您需求的类,并遵循最佳实践并使用最少的遗留方法。

每次调用的性能差异可能约为 0.5 us。这可能重要也可能不重要。

但是,如果您不需要类型是线程安全的,则没有充分的理由使用同步版本。如果你需要一个类型是线程安全的,你不能使用一个不是没有一些线程安全保护的类型。


小智 5

差异

  • HashMap 允许空值,Hashtable 不允许。
  • Hashtable 是同步的,HashMap 不是。(但如果不修改它仍然可以用于多线程读取,fe - 在启动时初始化一次,并且只为一些静态缓存读取它)

表现

这里有一些单线程测试来比较它们。100 次磨机操作中的 5 次尝试(第一次尝试可能被视为热身)放置是 100% 碰撞,获取是 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
Run Code Online (Sandbox Code Playgroud)
  • 单线程
    HashMap 通常更快。它的get比 Hashtable快 2 倍。但是,它的put 慢了 25%

  • 如果不需要空值,则并发使用Hashtable,如果需要空值,则使用 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]);
    }
}
Run Code Online (Sandbox Code Playgroud)


Mik*_*ffe 0

是的。这是 HashMap 默认情况下不同步的(其中之一)(使用 synchronizedMap() 使其同步;尽管请注意,根据您的使用情况,仅简单的同步可能不足以保持您可能想要的所有操作的完整性)做)。