我们先将哈希表数据结构看成是这个样子:
那么整个map就是下图所示:
然后我们再来看put(key,value)和get(key)方法的实现原理。
map.put(key,value)实现原理:
第一步,先将 key,value 封装到 Node 对象中。
第二步,底层会调用k的hashCode()
方法得出 hash 值。
然后,通过哈希函数/哈希算法,将 hash 值转化为数组下标,下标的位置如果没有任何元素,就把 Node 添加到这个位置上了。如果说下标的位置上有链表,此时会拿着 key 和链表上的每一个节点中的 key 进行equals()
,如果所有的equals()
方法返回值都是false
,那么新节点将被添加到链表的末尾。如果其中有一个equals()
返回了true
,那么这个节点的 value 将会被覆盖。
v=map.get(key)实现原理
先调用 key 的hashCode()
方法得出哈希值,通过哈希算法转化成数组下标,通过数组下标快速定位到某个位置上(类似于查字典),如果这个位置什么也没有,返回 null 。如果这个位置上有单向链表,那么会拿着参数 key 和链表中的每个节点的 key 进行equals()
,如果equals()
方法返回false
,那么 get 方法返回 null 。只要其中有一个节点的equals()
方法返回了true
,那么此时节点的 value 就是我们要找的值,get
方法最终返回这个节点的 value 。
为什么哈希表的随机增删,以及查询效率都比较高?
增删是在链表上完成。
查询不需要全部都扫描,只需要部分扫描。
重点:通过讲解可以得出 hashMap 集合的 key ,会先后调用两个方法,一个方法是hashCode()
方法,一个是equals()
方法,那么这两个方法都要重写。
HashMap部分特点
无序不可重复
为什么无序?因为不一定挂到哪个单向链表上
不可重复是怎么保证的?equals方法来保证HashMap集合中的key不可重复
如果key重复了,value会覆盖
放在HashMap集合key部分的元素其实就是放到了HashSet集合中了
所以HashSet集合中的元素也需要同时重写HashCode()和equals()方法
哈希表使用不当时会无法发挥其性能!
假设将所有的hashCode()
方法的返回值固定为某个值,那么会导致哈希表变成纯单向链表。这种情况我们称为:散列分布不均匀。
同理,假设将所有的hashCode()
方法的返回值设定为都不一样的值,那么会导致哈希表变成一维数组,也是散列分布不均匀。
HashMap的初始化容量为 16,默认加载因子是 0.75。
这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容。扩容之后是原容量 × 2
这里提一下HashTable的初始容量是11,每次扩容是原容量 × 2+1
重点,记住:HashMap集合初始化容量必须是2的倍数,这也是官方推荐的,这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必须的。
注意:JDK8后对HashMap进行了优化,当单向链表上的节点的个数超过8个时,链表会变成二叉树 / 红黑树。当节点个数小于6个时,二叉树会重新转换为单向链表。