Java 集合框架之HashMap
集合:集合是指一组对象规范,Map不符合这个规范,因此Map不是集合只是集合框架的一种实现。
哈希表
哈希表(也叫散列表),是根据关键码值(key、value)而直接访问的数据结构。
开散列法
首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。该算法产生的桶即哈希桶
哈希冲突
通过哈希函数计算出的索引相同当关键字不同,由此产生了哈希了冲突。如何解决:通过链表的形式存储哈希值相同的元素。
红黑树
树
树是一系列包含父子关系的节点,节点通常可划分为三种,根节点,内部几点,叶节点
二叉查找树
1.树的左子树上所有节点的值均小于根节点的值
2.树的右子树上所有节点的值均大于根节点的值
3.任意节点的都可以满足1,2条件
4.不存在键值相等的节点
平衡二叉树
平衡二叉树一般是指平衡二叉查找树,它是在二叉查找树上规定,任意一个节点它的左子树和右子树的高度差的绝对值不能超过1。
- 平衡二叉树左旋
左旋是将右子节点晋升为父节点,同时将右子节点的左子树让给降级后的父节点成为它的右子树
- 平衡二叉树右旋
右旋是将左子节点晋升为父节点,同时将左子节点的右子树让给降级后的父节点成为它的左子树
红黑树
红黑树是一个趋近于平衡二叉树的数据结构,红黑树需要满足以下特性
-
特性
- 节点是红色或黑色
- 根节点时黑色
- 每个叶子节点都是黑色的空节点(NIL节点)
- 每个红色节点的两个子节点都是黑色
- 从任一节点到其叶子节点中包含的黑色节点都是相等的
-
如何平衡
- 左旋:左旋是将右子节点晋升为父节点,同时将右子节点的左子树让给降级后的父节点成为它的右子树
- 右旋:右旋是将左子节点晋升为父节点,同时将左子节点的右子树让给降级后的父节点成为它的左子树
- 变色:节点的颜色由黑变红或者由红变黑
HashMap
数据结构
- JDK1.7 数组+链表
- JDK1.8 数组+链表+红黑树
初始容量
-
为什么需要是2的n次方幂?
保持元素的分布均匀及空间的利用率,我们一般会采用取模操作。而Java是通过与运算来完成。当容量是2的n次幂方时,会使用hashcode & (length-1) 来计算位置,而2的n次幂方减1之后变成二进制全部是1,执行与操作和取余操作是一样的结果。
加载因子
-
什么是加载因子?
加载因子是控制哈希表中元素的填满程度。加载因子越大空间利用率越高当冲突机会也会增大,加载因子越小冲突机会减小但会存在空间浪费。
-
HashMap中的加载因子
在HashMap中会结合容量和加载因子,控制HashMap的扩容,当加载因子*容量小于元素的个数时,需要进行扩容
扩容
-
什么时候触发扩容?
当实际容量大于加载因子*容量时,会触发扩容
-
扩容会产生什么问题?
-
造成死锁
在JDK1.7中扩容是将原来的所有元素全部取出,并重新计算hashCode,原来的顺序会变成倒序如果两个线程同时在进行扩容就容易造成循环列表,当下一次查找元素时即会触发死锁
在JDK1.8中扩容采用将元素的hashCode根据当前容量的和扩容后的容量分为高位和低位(低位需要一致),如果该元素高位是1则晋升到扩容的数组,如果该元素高位为0则留在当前数组并且保持原有的顺序,尽可能的避免死锁
-
数据安全性
由于Tomact 在使用JDK1.7时如果URL带参数时,恶意构造一系列相同hash值时,hashmap退化成链表,进而影响CPU的使用率。
-
红黑树
-
什么时候链表进化成红黑树,什么时候退化成链表?
当阈值超过8时,当阈值小于6时退化成链表
-
为什么阈值是8?
满足泊松分布(Poisson distribution)