一. 简介
- 散列表也叫哈希表,是存储Key-Value映射的集合。通过哈希函数实现Key和数组下标的转换,通过开放寻址法和链表法解决哈希冲突,且读写操作时间复杂度接近O(1)。
二. 原理
-
哈希函数
散列表是基于数组的,通过哈希函数把Key和数组的下标进行转换。哈希函数的实现方式有很多种,以HashMap中的哈希函数为例:每个对象都有属于自己的hashcode(唯一性),而hashcode都是一个整型变量,再由hashcode对数组长度取模运算(
index = HashCode(Key) % Array.length
),实现Key转换为数组的下标index。 -
散列表的读写操作
(1)写操作(put)
写操作即插入新的键值对(在JDK中叫做Entry)。如调用hashMap.put(“002931”,“小明”)的步骤:
a. 通过哈希函数,把Key转换为数组下标5;
b. 若数组下标为5的位置没有元素,就把这个Entry填充到此位置;
c. 若数组下标为5的位置有元素了(即发生了我们常说的哈希冲突
,解决方法有:开放寻址法、链表法
),HashMap采用了链表法解决哈希冲突,先判断此链表中是否有了这个Key,如果有了此Key则用新的value值代替旧的value值,如果没有此Key,则将键值对插入到链表头。【拓展】解决哈希冲突的两种方法
开放寻址法
:当一个Key通过哈希函数获得对应的数组下标被占用时,寻找下一个空档位置。
链表法
:当一个Key通过哈希函数获得对应的数组下标被占用时,先判断此链表中是否有了这个Key,如果有了此Key则用新的value值代替旧的value值,如果没有此Key,则将键值对插入到链表头。(2)读操作(get)
读操作即通过给定的Key,在散列表中查找对应的值。如调用hashMap.get(“002931”)的步骤:
a. 通过哈希函数,把Key转换为数组下标5;
b. 找到数组下标为5所对应的元素,如果这个元素的Key是002931,就找到了;若Key不是002931,顺着链表往下找,找到匹配的节点即可。(3)扩容 (resize)
a. 影响扩容的因素:
Capacity:即HashMap的当前长度;
LoadFactor:即HashMap的负载因子,默认是0.75f。b. 衡量扩容的条件:
HashMap.Size >= Capacity x LoadFactor
c. 扩容的步骤
扩容
:创建一个新的Entry空数组,长度是原来数组的2倍;
重新Hash
:遍历原Entry数组,把所有的Entry重新Hash到新数组中,其目的就是将原来的Entry尽可能均匀的分配。