1、HashMap简介
HashMap基于哈希表的Map接口实现,是以key-value存储形式存在。JDK1.8版本中其数据结构是:数组+链表+红黑树的形式。下面详解源码及解决的问题。
2、HashMap类
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
接口方法:
- put方法(新增 )
- resize方法(扩容 )
- get方法(遍历)
- remove方法(删除)
- treeifyBin方法(链表树化)
- untreeify方法(红黑树转链)
成员变量:
- table变量:HashMap的底层是Node类的实体数组,Node是一个静态内部类,一种数组和链表相结合的复合结构,用于保存key-value对;
- size变量:表示已存储的HashMap的key-value对的数量;
- loadFactor变量:装载因子,默认值为0.75f(DEFAULT_LOAD_FACTOR = 0.75f;);
- threshold变量:临界值,超过此值hashmap自动扩容,threshold=容量*加载因子;
- capacity:容量,非成员变量,默认值是16;
3、HashMap方法详解
1.put方法源码及总结
总结:
- 判断HashMap中的数组是否为空或者大小为0,如果是则初始化数组。
- 如果该hash值对应的数组中的位置为空,则将该数据组成的节点直接插入到该位置中。
- 如果数组对应位置数据不为空,判断该位置节点的key与要插入的key是否相等,如果是设置e(局部变量)等于该节点。
- 如果Node结构为树结构,则遍历树结构找到key对应的节点,设置为e。
- 如果Node结构为链表,遍历链表,如果链表中没有找到对应的key,将数据构造成Node节点插入链表最后,如果链表长度大于等于8,则将结构转为树。
- 如果在链表中找到对应的key,则将该节点设置为e.
- 如果e(上面遍历找到的节点)不为null,则设置新的value,返回旧的value.
- 如果e为null,说明是新增,HashMap大小加一,判断是否大于阈值,如果大于,则扩容。
2.get方法源码及总结
总结:
- 根据key的hash值找到数组中对应的位置。
- 判断该位置上的值和要查找的值是否相等(==或者equals),如果是则返回
- 如果不是则判断该节点的下一个节点是否为空,为空则返回null。
- 判断结构是否是红黑树,如果是,遍历树。
- 如果不是树,则遍历链表。
- 如果不符合上面的条件则返回null。
3.resize(扩容)方法源码及总结
总结:
- 数组是否已经达到最大值,如果已经最大,设置阈值也为最大值,否则数组大小和阈值都改为之前的两倍。
- 数组是否已经初始化,如果没有则初始化数组和阈值。
- 旧数组不为null,遍历旧数组,将对应位置的链表/树分为成两个链表/数组,一个在原先的位置上,一个在原先的位置+原先数组大小的位置上,将两个链表/树放入新数组的对应位置。
jdk1.8的优化
由JDK1.7的,数组 + 链表
JDK1.8变为:数组 + 链表 + 红黑树
具体触发条件为:某个链表连接的个数大于8,并且总的容量大于64的时候,那么会把原来的链表转换成红黑树。这么做的好处是什么:红黑树除了添加元素外,查询和删除效率比链表快。
红黑树查询、增加和删除的时间复杂度:O(log2n)
链表的查询和删除的时间复杂度: O(n),插入为:O(1)