HashMap简介
HashMap是Java中最常用的集合类框架,是Java语言中非常典型的数据结构,也是Java程序员使用频率最高的用于映射(键值对)处理的数据类型。
键值对:Entry(Key , Value)
HashMap在获取元素时,可以通过Key来获取Value。HashMap有如下特点:
- 根据键的HashCode值存储数据
- 只允许一条记录的键为null,允许多条记录的值为null(允许null值)
- 线程不安全
- 底层是HashTable(哈希表)
- 遍历无序
- 初始长度为16
- 默认加载因子0.75
数组和链表的特点
数组:
- 连续存储
- 查找效率高
- 添加、删除操作效率较低
链表:
- 离散存储
- 添加、删除操作速度快,扩展灵活
- 不能随机查找
HashMap底层原理
- HashMap底层实际上是数组+链表的结合,它结合了数组和链表的优点,使得查找、添加、删除操作的效率都较高。
- 我们知道HashMap是一个用于存储Key-Value键值对的集合,每一个键值对也叫做Entry。这些键值对分散存储在一个数组当中,这个数组就是HashMap的主干。数组元素的每一个Entry都是一个链表的头结点,链表主要为了解决哈希冲突而存在。
map.put(K,V)实现原理
- 例如,我们现在需要插入一个Key为“Student”,Value为“杰哥”的元素,此时的键值对Entry为(Student,杰哥)。调用方法map.put(“Student”,“杰哥”)将Entry添加到HashMap。
- 调用方法后,HashMap底层调用Key的HashCode方法得出Hash值,并将Hash值转换为数组的下标(index)。
- 假设下标值为“2”,如果2这个位置上没有任何元素,则把键值对添加到“2”这个位置。此时数组中的结构如下图:
- 如果我们又添加了另一个Entry,而计算出它的下标值也正好是“2”,此时数组中“2”的位置上已经有了之前添加的一个Entry,这样就会造成冲突。解决这种情况的办法就是使用链表。
- 第一个Entry为链表头结点,发生冲突时将第二个Entry插入至链表即可,插入时使用的是头插法,因为HashMap的发明者认为,后插入的Entry被查找的可能性更大,插入后的HashMap如下图:
- 在插入链表时会使用Key和链表上每个节点的k进行equal。如果所有的equals方法返回都是false,那么这个新添加进来的结点将正常添加。如其中有一个equals返回了true,那么这个节点的value将会被覆盖。
map.get(K)实现原理
- 假设我们要通过Key“Student”找到Value“杰哥”,调用方法map.get(“Student”)。
- HashMap底层调用Key的HashCode方法得出Hash值,并将Hash值转换为数组的下标(index),假设为“2”。
- 通过下标快速定位某个位置,并与该位置Entry的Key值进行equals,如果为false,则证明此位置无内容。
- 如果这个位置有链表,则会与该链表每个结点的Key进行equals,如果返回true,则表示找到对应Key值的结点。
- 最终get方法返回该位置的value。
其它
在JDK1.8之后会有些许变化:
- 链表插入时从头插法变更为尾插法
- 链表长度超过8时会转换为红黑树
- 当红黑树上的节点数量小于6个时,会重新把红黑树变成单向链表数据结构