为什么我在自定义的闭合哈希集中会遇到这么多冲突?

时间:2012-03-26 14:05:54

标签: java hashmap hashtable hash

我有一个自定义的closed-hashset / open-addressing(即没有链接列表)类。它非常特定于我的需求 - 它不是通用的(仅适用于正长数字),需要插入预定义的记录数量,并且不支持删除 - 但它是意味着尽可能少占用空间。

由于功能如此之少,它是一个非常小而简单的类。但是由于某些原因,当我插入许多条目时,碰撞次数变得太快太快了。

一些代码(Java):

public class MyHashSet
{
    private long[] _entries;

    public MyHashSet(int numOfEntries)
    {
        int neededSize = (int)(numOfEntries / 0.65D);
        _entries = new long[neededSize];
    }

    public void add(long num)
    {
        int cell = ((Long) (num % _entries.length)).intValue();

        while (_entries[cell] != 0)
        {
            if (++cell >= _entries.length)  
                cell = 0;                   
        }

        _entries[cell] = num;
    }
...

我有一个main,它以1000万作为参数来实现MyHashSet对象,然后使用不同的随机生成(但是正数)长号调用add()1000万次。在普通的Java HashSet上,这个插入整个过程大约需要一秒钟,完成MyHashSet大约需要13秒。 我添加了一个碰撞计数器,事实上,碰撞的数量是3-6亿 - 远远超过预期(我估计大约有3到4,000万人)。

我做错了吗?散列本身有什么问题吗?为什么会有这么多碰撞,我该怎么办呢?

谢谢!

P.S。:代码中的数字0.65表示该表只能填充65%,我知道它应该在闭合的哈希集中运行良好。对于这个问题,即使我将其设置为20%,插入仍然需要> 10秒..

- 编辑 -

这非常难以承认,但是我的测试代码在循环的每次迭代中重新创建了Random对象(使用System.currentTimeMillis()作为种子),而不是在整个运行中使用相同的对象。

固定后,插入完成大约需要2-3秒。相比之下,这仍然看起来太多了 - 为什么默认的java HashSet只需要一秒钟就可以插入,当它更复杂的时候#39;比MyHashSet?我现在只能获得大约9百万次碰撞。我还尝试关闭日志记录代码以查看它是否有帮助,但它仍然无法弥补差异。我很欣赏任何想法,对此之前的混淆再次感到抱歉。

2 个答案:

答案 0 :(得分:3)

我注意到的第一件事是在线上免费拳击

int cell = ((Long) (num % _entries.length)).intValue();

慢得多
int cell = (int) (num % _entries.length);

(请注意,num % _entries.length始终适合int,因为_entries.length本身就是int。)

诚然,Java的HashSet无论如何都会遇到类似的开销,但这至少是一个明显的问题需要解决。

此外,确保表格大小为素数可能对您有利。最简单的方法是BigInteger.valueOf((int)(numOfEntries / 0.65)).nextProbablePrime().intValue(),因为它是一次性成本,所以不应该太严重地影响整体性能。

或者,Java的HashSet使用2次幂哈希表大小,因此它可以使用掩码(value & (_entries.length - 1),基本上)而不是%,这通常更昂贵。

答案 1 :(得分:1)

首先:修复你的模数函数。否则你将获得ArrayOutOfBounds异常,并且很容易修复,没有真正的性能成本(只是一个和)。此外,如果你在这里,做路易斯提出的建议并摆脱无用的长期演员。

无论如何,真正的问题是,如果细胞已被摄取,你正在使用可怕的下一个功能。线性探测通常是一个坏主意,然后你甚至只是朝着一个方向变得更糟。如果你的数字没有完全统一排列,你会发生很多冲突。双哈希在实践中非常有用,但你也可以修复线性探测并测试是否有帮助。

那么你应该使用表格大小的素数作为路易斯提出的,它具有一些(理论上可证明的)优点但速度较慢,或者使用2的下一个幂。目前你正在结合两种方法的缺点