HashMap的底层以数组+单向链表/红黑树存储。数组一个位置可以存在多对数据,这些数据以链表或红黑树的形式存储。加入红黑树的存储方式是在JDK8.0。
创建数组时,其初始化长度为16(JDK7.0和JDK8.0初始化数组的时机不同)。添加数据时,首先调用数据的key所属类的hashCode()方法计算出哈希值,哈希值经一定的变换求得该数据在数组的位置。
得到数据在数组中的位置后,进入以下一系列的判定:
- 若该位置为空,不存在任何数据。
- 直接添加数据。
- 若该位置已存在一个或多个元素,进入下一个判定:
- 对该位置已存在的元素的key值进行遍历
- 所有元素key的哈希值与欲添加的数据key的哈希值不同
- 添加数据到链表的末尾(Java8及以后)。
- 添加数据到链表的开头(Java7及以前)。
- 存在某些元素key的哈希值与欲添加数据key的哈希值相同,此时,调用equals()方法,进入下一个判断:
- 哈希值相同,但equals()都返回false
- 添加数据到链表的末尾(Java8及以后)。
- 添加数据到链表的开头(Java7及以前)。
- 哈希值相同,并且存在equals()返回true的情况
- 判断欲添加的数据的key值在集合中已存在,不予添加。
- 哈希值相同,但equals()都返回false
- 所有元素key的哈希值与欲添加的数据key的哈希值不同
- 对该位置已存在的元素的key值进行遍历
HashMap存在填充因子、扩容临界值等属性。扩容临界值=数组容量*填充因子。当数组中不为空的位置个数大于扩容临界值时,进行扩容操作。(并不是数组满了才扩容)
扩容操作:创建新的数组,长度为旧数组的2倍,将原来的元素复制过来。
在JDK8.0中,当链表过长,且数组容量达到一定标准时,索引位置上的数据将转换成以红黑树的形式存储,提高了遍历的效率。
注:以上笔记是笔者自己的理解,并非标准定义,如有错误,欢迎指出,不甚感激!