散列表(中)

如何设计这样一个工业级散列表呢?我会从3个方面来考虑设计思路:

1.设计一个合适的散列函数;
2.定义装载因子阈值,并且设计动态扩容策略;
3.选择合适的散列冲突解决方法。

一、如何设计散列函数?

1.要尽可能让散列后的值随机且均匀分布,这样会尽可能减少散列冲突,即便冲突之后,分配到每个槽内的数据也比较均匀。
2.除此之外,散列函数的设计也不能太复杂,太复杂就会太耗时间,也会影响到散列表的性能。
3.常见的散列函数设计方法:直接寻址法、平方取中法、折叠法、随机数法等。

二、如何根据装载因子动态扩容?

如何设置装载因子阈值?

1.可以通过设置装载因子的阈值来控制是扩容还是缩容,支持动态扩容的散列表,插入数据的时间复杂度使用摊还分析法。
2.装载因子的阈值设置需要权衡时间复杂度和空间复杂度。如何权衡?如果内存空间不紧张,对执行效率要求很高,可以降低装载因子的阈值;相反,如果内存空间紧张,对执行效率要求又不高,可以增加装载因子的阈值。

如何避免低效扩容?分批扩容

1.分批扩容的插入操作:当有新数据要插入时,我们将数据插入新的散列表,并且从老的散列表中拿出一个数据放入新散列表。每次插入都重复上面的过程。这样插入操作就变得很快了
2.分批扩容的查询操作:先查新散列表,再查老散列表。
3.通过分批扩容的方式,任何情况下,插入一个数据的时间复杂度都是O(1)。


分批搬移数据

三、如何选择散列冲突解决方法?

  • 常见的2中方法:开放寻址法和链表法。
  • 大部分情况下,链表法更加普适。而且,我们还可以通过将链表法中的链表改造成其他动态查找数据结构,比如红黑树、跳表,来避免散列表时间复杂度退化成O(n),抵御散列冲突攻击。
  • 但是,对于小规模数据、装载因子不高的散列表,比较适合用开放寻址法。

分析Java的HashMap:

初始大小

hashMap默认的初始大小是16.

装载因子和动态扩容

最大装载因子默认为0.75,当hashMap中元素个数超过0.75*capacity(capacity表示散列表的容量)的时候,就会启动扩容。每次扩容都会扩容到原来的两倍。

散列冲突解决方法

使用单链表解决hash冲突,当链表长度大于8,将单链表转换成红黑树 , 当链表长度小于6,将红黑树退化为单链表。

散列函数

hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

你可能感兴趣的:(散列表(中))