红黑树是什么?
三大特点:
-
根节点是黑色,叶节点是不存储数据的黑色空节点
-
任何相邻的两个节点不能同时为红色
-
任意节点到其可到达的节点间包含相同数量的黑色节点
联想:Java HashMap底层红黑树原理
HashMap基于哈希表Map接口实现,是以key-value存储形式存在,即主要用于存储键值对。
HashMap特点:
-
存取无序
-
键和值位置都可以为null,但是键的位置为null只能有一个
-
键位置是唯一的,底层数据结构控制键
哈希冲突:
定义:两个对象调用的hashCode方法计算的哈希码值一致导致计算机的数组索引值相同
HashMap底层数据结构
JDK1.8之前,HashMap是由数组+链表组成的,数组是HashMap主体,链表用来解决哈希冲突。即,发生hash冲突后使用equals判断是否相等,相等则存储在该节点的链表中。
JDK1.8之后,当链表长度大于阈值(或者红黑树的边界值,默认为8),并且当前数组长度大于64时,此时索引位置上的所有数据改为红黑树存储。
创建对象底层原理:
HashMap<String,Integer> map = new HashMap<>();
-
当创建HashMap集合对象时,
-
在JDK8前,构造方法中创建一个长度是16的Entry[] table 用来存储键值对数据的
-
在JDK8后,不是在HashMap的构造方法底层创建数组了,而是在第一次使用put方法时,创建的数组,Node[] table用来存储键值对数据的
-
-
存储数据数组位置为空时:
-
比如将一个键值对 (“Benaso” ,1) 存储到哈希表中,根据 “Benaso” 调用 String 类中重写之后的hashCode() 方法计算出值,然后结合数组的长度采用某种算法算出向Node数组中存储数据的空间值索引,如果该索引对应空间没有数据,则将 (“Benaso” ,1) 存储到数组中
-
面试题:哈希表底层采用何种算法计算hash值?还有哪些算法可以计算出hash值?
-
底层采用key的hashCode方法的值结合数组长度进行无符号右移(>>>)、按位异或(^),按位与(&)计算出索引。(异或:两个二进制值相同为0,不同为1)
-
还可以采用平方取中法,取余数,伪随机数法
-
哈希表采用方法原因是与其他方法相比该方法效率高
-
-
-
存储数据数组位置不为空时:
-
例如向hash表新存一组数据(“Victor” ,1),假设根据Victor计算出的hashCode方法结合数组长度计算出的索引值也是3,那么此时数组空间不是null,此时则会比较Victor的hash值是否一致,如果不一致,则在此空间上划出一个节点来存储键值对数据 (“Victor” ,1)。——这种方法被称为拉链法
-
-
假设向哈希表中存储数据 (“Benaso” ,2) 那么根据Benaso调用hashCode方法结合数组长度计算出索引也为3,此时对比较后存储的数据Benaso和已经存在的Benaso的hash值是否相等,如果相等,发生hash碰撞:
-
相等:则将后添加的数据的value覆盖到其上
-
不相等:那么继续向下和其它数据的key进行比较,如果都不相等,则划出一个节点存储数据。
-
-
一般不会等到存储数据到达16才扩容,
threshold(临界值)= capacity(容量) * loadFactor(加载因子)
这个值是当前已占用数组长度的最大值,size超过这个临界值就重新resize(扩容),扩容后的 HashMap 容量是之前容量的两倍。
HashMap集合类的成员
成员变量
集合的初始化容量(必须是二的n次幂)
//默认初始容量是16 -- 1<<4相当于1*2的4次方 --- 1*16 static final int DEFAULT_INITAL_CAPACITY = 1 << 4
HashMap扩容
扩容条件
-
元素个数超过12扩容,
-
链表节点大于8,数组长度小于64
扩容后索引要么是原索引,要么是原索引加16
-
计算新的索引高位是0那么存储到原来索引位置
-
如果高位是1那么存储到原来索引加旧的数组的长度