HashMap底层实现原理
HashMap简介
HashMap底层是哈希表/散列表的数据结构,哈希表是数组和单向链表的结合体。数组查询效率高,随机增删效率低;单向链表查询效率低,随机增删效率高。哈希表将这两种数据结构融合在一起,充分 发挥它们各自的优点。
HashMap底层源码
public class HashMap<K, V> {
Node<K, V>[] table;
static class Node<K,V> {
final int hash;//哈希值(哈希值是key的hashCode()的执行结果,哈希值通过哈希算法可以转换为数组的下标)
final K key;//map集合中的key
V value;//map集合中的value
Node<K, V> next;//下一个节点的内存地址
}
······
}
HashMap的put()方法
先将key和value封装到Node对象中,底层调用key的hash(key)方法算出hash值,然后通过hash算法将hash值转换为数组下标。如果下标位置上没有元素就把Node添加到该位置上;如果下标位置上有链表,此时会拿着当前Node中的key与链表中的每一个Node中的key进行equals()比对,如果所有的比对结果都为false,那么当前Node添加到链表的末尾
(jdk8以前是头部),如果其中有一个比对结果返回了true,那么会将链表中的这个Node中的vaule将会被覆盖。
jdk8以后,当单项链表上的元素达到8个时,会将单向链表转换为红黑树。当红黑树上的元素少于6个时,会将红黑树转换为单向链表。这种方式也是为了提高检索效率。
HashMap的get(key)方法
先调用key的hash(key)方法得出hash值,然后通过hash算法将hash转换为数组下标,通过数组下标快速定位到数组的某个位置上,如果这个位置上什么也没有返回null。如果这个位置上有单向链表,会拿着这个key和单向链表中每个Node进行equals()比对。如果所有比对结果为false,那么get()结果返回null;如果其中有一次比对结果返回true,那么get()结果将返回这个Node的value。
hash算法
在HashMap中要找到某个元素,需要根据key的hash值来求得对应数组中的位置。如何计算这个位置就是hash算法。
//计算key的hash值
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
获得hash值后,用数组的长度-1与(&)该hash值得到key在数组中对应的位置。
HashMap扩容
HashMap的默认初始化容量为16,默认加载因子为0.75(当HashMap容量达到75%时底层数组开始扩容,扩容后的容量为原容量的2倍)HashMap的初始化容量必须是2的次幂,这也是官方推荐的,这也是达到散列均匀和提高存取效率所必需的的
默认情况下,数组大小为16,那么当hashmap中元素个数超过16×0.75=12的时候,就把数组的大小扩展为2×16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高hashmap的性能。
为什么哈希表的查询效率和随机增删效率都很高?
1、查询不需要全部扫描,只需要部分扫描。
2、增删是在链表上完成
HashMap的key部分特点
无序、不可重复
为什么无序?
不一定会挂到哪一个单向链表上 。
如何保证不可重复?
equals()方法保证了不可重复,如果key重复了value会覆盖。