HashMap
1. HashMap简介
- HashMap是在JDK1.2中引入的Map的实现类。
- HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长。
- HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。
- HashMap 实现了Serializable接口,因此它支持序列化,实现了Cloneable接口,能被克隆。
- HashMap中key和value都允许为null。但只能有一条记录可以是一个空的key;任意数量的条目可以是空的value
2. 技术本质
2.1 数据结构
jdk 1.7:数组+链表
-
数组(ArrayList):采用一段连续的存储单元来存储数据。
- 特点:查询速度快,插入删除速度慢
- 查询时间复杂度O(1) ;删除插入时间复杂度O(N)
-
链表(LinkedList):是一种物理存储单元上非连续、非顺序的存储结构
- 特点: 插入删除速度快,查询速度慢
- 删除插入时间复杂度O(1);查询时间复杂度O(N)
- 特点: 插入删除速度快,查询速度慢
jdk 1.8:数组+链表+红黑树
链表长度>=8时,链表转变为红黑树
红黑树简介:
自平衡的二叉树
-
特点:
1,所有的节点非黑即红
2,根节点一定是黑的
3,红色节点的子节点一定黑节点,黑节点的子节点可以是红节点, 也可以是黑节点
4,最底层的叶子节点一定是黑色的空节点
5,从根节点到任意一个叶子节点所经过的黑色节点的个数一定相同,黑色的节点的高度一致。、
6,新添加的元素一定是红色的。 -
修正:
- 涂色:当前节点为红色,父节点也为红色,并且叔父节点也为红色,那么需要将父节点以及叔父节点涂黑,并且将祖父节点涂红
- 左旋:当前节点为红,父节点为红,叔父节点为黑,并且当前节点为右子叶,那么需要以当前节点为轴进行左旋
- 右旋,当前节点为红,父节点为红,叔父节点为黑,并且当前节点在左子页,那么需要以父节点为轴进行右旋
2.2 算法
-
哈希算法(散列):
- 把任意长度值(key)通过散列算法变换成固定长度的key(地址),通过这个地址进行访问的数据结构。
- 把关键码值映射到哈希表中,通过哈希表中的位置来访问记录,加快了查找的速度。
-
哈希算法底层原理:
(1) Hashcode:通过字符串算出它的ASCII码,进行mod(取模),算出哈希表中的下标。
取模是为了节省空间
(2) 哈希冲突时,要用链表 。
当两个字符串的ASCII码值相同时,往数组中存放时,就会发生哈希冲突。使用链表来解决此问题。
(3) 链表长度>=8时,链表转变为红黑树
3. HashMap和HashTable的区别
| HashMap | HashTable | |
|---|---|---|
| JDK版本 | JDK1.2版本 | JDK1.0版本 |
| 父类 | AbstractMap | Dictionary |
| 线程是否安全 | 线程不安全 | 线程安全 |
| 效率 | 高 | 低 |
| 是否允许为null值 | 允许key和value为null值。可以用containsKey()方法来判断。 | 键值对都不能为空,否则包空指针异常 |
| 是否提供contains方法 | 修改为containsValue和containsKey方法 | 保留了contains方法 |
| 计算hash值方式 | 首先需要根据元素的 KEY计算出一个hash值,然后再用这个hash值来计算得到最终的位置 | 直接使用对象的hashCode |
| 遍历方式 | Iterator | Iterator、Enumeration |
| 数组默认大小 | 16 | 11 |
| 扩容方式 | 要求一定为2的整数次幂,容量变为原来的2倍 | 容量变为原来的2倍加1 |
相同点:
- 都是基于哈希表实现的
- 每个元素是一个key-value对
- 内部都是通过单链表解决哈希冲突问题
- 二者都实现了Map接口。
- 都实现了Serializable接口,支持序列化
- 都实现了Cloneable接口,能被克隆。