有关LinkedList节点的HashTable性能的问题

时间:2018-11-08 23:36:52

标签: java data-structures hashtable

我在Class的初始化时实现了一个具有可变大小存储桶的HashTable,只是一个在运行时调整大小的链表数组。

问题在于,在必须遍历链表的存储桶数量较少(深度可以达到约5K节点)的情况下,HashTable的性能超过了HashTable,其中更多的存储桶相差三个数量级。

    int SMALL_BUCKET_SIZE = 10;
    int BIG_BUCKET_SIZE = 10000;

    HashTable<String, Integer> smallHashTable = new HashTable<>(SMALL_BUCKET_SIZE);
    HashTable<String, Integer> bigHashtTable = new HashTable<>(BIG_BUCKET_SIZE);

我希望较大的HashTable对于搜索来说是O(1),其中较小的哈希表具有较高的冲突率,由于遍历链接节点而花费更多的时间,但是我的以下数字显示较小的表胜过较大的哈希表桌子。

Fetch SmallTable: 0.000007
Fetch BigTable: 0.000018

因此,我决定循环HashTable.get一千次以考虑JIT和JVM优化。现在,我开始看到似乎可以证实我期望的数字。

Fetch SmallTable: 0.0000013630
Fetch BigTable: 0.0000002560

我的问题是我的逻辑以及此处其他活动部件的合理性。我将测试粘贴到了HashTable和底层Node结构的实现的链接上。

从这里的人们那里寻找深度/经验,他们也许能够提供有关变量的交互式反馈,例如变量的长度,哈希碰撞率,存储桶密度等。

HashTableTest.java

@Test
public void canInitializeHashTableWithBucketsForPerformance() throws InterruptedException {
    double smallTableTime, bigTableTime;
    int SMALL_BUCKET_SIZE = 10;
    int BIG_BUCKET_SIZE = 10000;

    HashTable<String, Integer> smallHashTable = new HashTable<>(SMALL_BUCKET_SIZE);
    HashTable<String, Integer> bigHashtTable = new HashTable<>(BIG_BUCKET_SIZE);
    List<String> strings = generateRandomStringKeys(1000);

    strings.forEach(string -> bigHashtTable.put(string, 10));
    strings.forEach(string -> smallHashTable.put(string, 10));

    Consumer<String> bigHashGet = bigHashtTable::get;
    Consumer<String> smallHashGet = smallHashTable::get;

    String theString = strings.get(strings.size() - 1);

    smallTableTime = getElapsedTimeFactoringOutJavaOptimization(theString, smallHashGet);
    bigTableTime = getElapsedTimeFactoringOutJavaOptimization(theString, bigHashGet);

    System.out.println(String.format("Fetch SmallTable: %.10f", smallTableTime));
    System.out.println(String.format("Fetch BigTable:   %.10f", bigTableTime));

    assertTrue(smallTableTime > bigTableTime);
}

public double getElapsedTimeFactoringOutJavaOptimization(String s, Consumer<String> aMethod) {
    long start = 0, end = 0;

    for (int i = 0; i < 1000; i++) {
        start = System.nanoTime();
        aMethod.accept(s);
        end = System.nanoTime();
    }

    return (end - start) / 1_000_000_000D;
}

public List<String> generateRandomStringKeys(int numOfRandomKeys) {
    List<String> keys = new ArrayList<>();

    for (int i = 0; i < numOfRandomKeys; i++) {
        byte[] array = new byte[10];
        new Random().nextBytes(array);
        keys.add(new String(array, Charset.forName("UTF-8")));
    }

    return keys;
}

可以在这里找到测试-Github - HashTableTest.java

也可以在此处找到实现-Github - HashTable.java

1 个答案:

答案 0 :(得分:1)

这里有很多错误,但其中包括:

  • 运行此操作1000次并为每个变量取MDI之差,将使基准测试无效。认真使用JMH。或至少运行一千万次。
  • 对于不同大小的表,您的哈希表实际上并没有任何不同。您使用nanoTime,这基本上意味着但是桌子很大,您仅使用10个存储桶,并假装其余的不存在。
  • table[getHash(key) % RADIX]并不是有用的哈希函数,尤其是在字符串上,尤其是当您希望实际找到其中存在的元素...或不存在时。
  • 使用它时,您没有将System.identityHashCode用作字段,因此最好将其删除。