1.Map
Map是Java集合相关的一个接口,主要是键值对的存储
,其元素需要重写hashCode()和equals()
Map的常用实现类有HashMap、HashTable、TreeMap
2.HashMap
HashMap和HashTable的元素无序、唯一
,底层结构都是数组+链表
HashMap的key可以存入null
,并且唯一;HashTable不允许key为null
HashMap线程不安全;HashTable是线程安全的
2.1.工作原理
- 首先通过
hashCode()计算key的hash值,对数组长度取余
得到存放数组的位置 - 遍历数组位置所在的链表,
比较key是否相等
,相等则替换value,返回原value- 比较hash值
- 比较地址值
- 调用equals()
- 判断是否需要
扩容
在JDK1.7中采用头插法;
在JDK1.8中采用尾插法,在链表的长度大于等于7时,如果数组长度 < 64,则进行扩容,否则进行树化
2.2.构造函数
JDK1.7
在JDK1.7中,使用无参构造时,默认数组长度为16,负载因子为0.75;
然后调整数组长度为2的整数倍
;确定数组扩容边界值:数组长度 * 负载因子
为什么数组长度必须是2的整数倍?
- 通过hash值对数组长度取余来确定数组位置时,使用了与运算&,效率比%更高;使用与运算的前提就是数组长度等于2的整数倍
h & (length - 1) 等效于 h % length
- 减少哈希冲突
负载因子为什么是0.75
- 数组扩容边界值 = 数组长度 * 负载因子
- 如果设置为1:
空间利用率高
;但哈希碰撞的几率也变高,而链表的查询效率较低
- 如果设置为0.5:哈希碰撞的几率低,产生链表的几率低,
查询效率高
;但扩容频繁,空间利用率低
- 所以在 0.5 ~ 1 之间,取中间值:0.75
JDK1.8
在JDK1.8中,使用无参构造时,负载因子默认为0.75,其余属性都是默认值
2.3.扩容
在扩容时,扩容至原数组长度的2倍
3.TreeMap
TreeMap的元素唯一,升序排列
;底层结构:红黑树
key必须实现比较器
- 无参构造时,使用内部比较器:key实现Comparable接口;
- 有参构造时,使用外部比较器:定义Comparator实现类
工作原理
- 第一次添加数据时,数据封装成新节点作为根节点root
- 第n次添加数据时,采用中序遍历,调用compare()比较key的大小,来选择合适的节点作为父节点;如果存在相等的key则替换并返回原value
- 为节点上色