首页 > 代码库 > 算法6-4:哈希表现状

算法6-4:哈希表现状

战争故事


非常久非常久以前,以前发生过非常多关于哈希函数的战争故事。

那些战争的基本原理就是通过精心构造造成大量的哈希冲突从而占用大量的CPU资源。

技术分享


被攻击的软件例有下面样例:

  • 带有漏洞的server:攻击者精心构造哈系冲突。仅仅须要56K的网速就能让server死机,从而达到DOS攻击的目的。

  • Perl 5.8.0:攻击者精心构造哈系冲突插入到关联数组中

  • Linux 2.4.20 内核:攻击者精心构造文件名称,造成大量哈系冲突从而让系统性能骤降。


攻击原理


在Java中的String对象非常easy构造哈系冲突。下图展示了Java中哈系冲突的样例。


技术分享


解决的方法


使用更加高级的哈系函数。避免冲突。比方md4 md5 sha0 sha1 sha2 whirlpool ripemd160。可是md4 md5 sha0 sha1眼下可以找到缺陷,关于MD5的冲突请戳这里:http://www.links.org/?

p=6


MD5不适合用于关联数组,由于开销太大。


两种办法的比較


眼下介绍了两种解决冲突的办法,各自是独立链表和线性探针。


独立链表:

  • 删除元素很方便

  • 随着数据量的添加。性能下降缓慢

  • 哈系冲突对系统的影响小


线性探针:

  • 使用更少的内存。由于没有链表

  • 更好的缓存性能


哈希函数的改进


眼下已经实现了非常多不同的哈希算法。


双值哈希:

一个哈希函数返回两个哈希值,插入元素时插入到较短的链条上。

这样的方法可以降低链条长度的期望值。


双重哈希:

使用线性探针方法,可是每次冲突之后跳过不同数量的元素来寻找空位。

这样的方法可以非常好地消除连续的占位。使得哈希表可以被差点儿填满,可是删除非常难实现。


Cuckoo哈希:

先产生一个哈希,计算出一个位置,假设有冲突。再添加一些參数继续哈希,计算出另外一个位置。直到找到空位位置。这样的方法的查找操作在最坏情况下复杂度是N。


哈系表和二叉树的比較


哈希表和平衡树都能够实现关联数组。


哈希表:

  • 代码简单

  • 这样的方法的性能最好

  • 对于简单的keyword来说这样的方法更快

  • 在java中有更好的系统支持,比方String中使用了hashCode的缓存


二叉树:

  • 更好的性能保障

  • 支持顺序操作(获取某个元素的名次、第N大的值、X到Y的元素数量等)

  • compareTo函数比hashCode函数更easy实现,不easy出错


Java库中对于这两种方法都有实现。

java.util.TreeMap  java.util.TreeSet是通过红黑树实现的,java.util.HashMap  java.util.IdentityHashMap是通过哈希表实现的。

 


算法6-4:哈希表现状