数据结构——4、散列表
1.1.1 *散列表*
散列表,即哈希表,这种数据结构提供了key和value的映射关系,在jdk中也叫做entry,只要给出一个key,就能查找出相应的value,查找的时间复杂度接近于O(1)
散列表本质上也是一个数组,数组是根据角标来查找元素的,散列表是根据key来查找元素的,且key以string类型为主
1.1.1.1 *哈希函数*
将key与数组下标进行转换的中转站就是哈希函数
如何进行中转:
无论对象自身是什么类型,它们的hashCode都是一个整型变量,将整形变量转换为数组下标就很容易了,按照数组长度进行取模运算
index=HashCode(key)%Array.length
jdk中的哈希函数不是直接进行取模运算,而是利用了位运算的方式来优化性能
1.1.1.2 *哈希冲突*
在散列表中插入新的entry,步骤:
1、 先要通过哈希函数,将key转化成数组下标
2、 如果数组下标没有这个元素,就将entry填充到这个下标所在位置;
如果数组下标有这个元素,就将entry追加到链尾
不同的key通过哈希函数获得的下标可能是相同的,即产生了哈希冲突,哈希冲突的解决方法包括两种:开放寻址法;链表法;
1.1.1.2.1 *开放寻址法*
当一个key通过哈希函数获得的数组下标已经被占用,就顺序查找下一个空挡位置,直到找到没有被占用的位置,
这种寻址方式是最简单的一种,可以有多种寻址方式
1.1.1.2.2 *链表法*
当一个key通过哈希函数获得的数组下标已经被占用,就直接追加到相应的链表的链尾即可
这里HashMap数组中的每一个元素不仅是一个entry对象,还是一个链表的头节点
1.1.1.3 *扩容*
数组的扩容,是直接在原来容量的基础上,扩大一倍,
散列表是基于数组实现的,那么也涉及到扩容问题,当拥挤在相同的数组下标位置,形成很长的链表,就会对后续插入和查询操作的性能有很大影响
影响散列表扩容的因素有两个:
1、 capacity:即hashmap的当前长度;
2、 loadFactor:即hashmap的负载因子,默认值是0.75f
衡量hashmap需要扩容的条件是:
HashMap.Size>=Capacity*LoadFactor
HashMap的扩容操作不是简单的将长度扩大,而是包括两个操作:
1、 扩容:创建一个新的entry空数组,长度是原来数组的2倍;
2、 rehashing:即重新hash,遍历原entry数组,将所有的entry重新hash到新数组中
为什么要重新进行hash?因为长度扩大之后,hash的规则也随之改变了,为了将元素尽可能均匀的分配
JDK1.8版本的hashmap和以前有很大的不同:
当多个entry被hash到同一个数组下标位置时,为了提升插入和查找的效率,hashmap会将entry的链表转换为红黑树结构