对hash的理解
将任意长度的输入,转化为固定长度的输出,会产生hash冲突,举例(有10个苹果,9个盒子,则肯定有一个盒子里面装两个)
好的hash算法应该有什么特点:首先效率要高,对长文本也能高效的计算出hash值;
不能根据hash值逆推出来原文;散列度较高输入有一点不同,得到的hash值就不同;极可能分散;
static final int hash(Object key) {
int h;
//将hashcode的高16位参与运算 防止散列
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
Node中的hash值不是key字段的hashcode值,而是key的hashcode经过二次hash后得到的结果hashcode的高16位与低16位异或操作后得到的
原因:因为大多数使用的hashmap的长度都不是很大,因此在进行寻址操作的时候(table.length - 1) & hash用的大多数都是低16位,因此将让hashcode的高16位也参与运算,减少hash冲突
JDK8链表 数组 红黑树
hashmap里面存储的元素是Node类型,里面包括key value hash next
3.创建hashmap默认长度为16
第一次put数据的时候会进行hashmap的创建,默认长度为16,负载因子为0.75,第一次扩容阈值为12
4.链表转化为红黑树的条件是,链表长度到达8,并且散列表数组的长度到达64,否则的话,就算链表长度到达8,也不会转化为红黑树,只会触发扩容操作resize();
put操作
首先都是进行hash算法,计算出具体的hash值,然后再根据寻址算法,计算出具体的桶位。
计算出具体的桶位后,分4种情况1.桶位没有元素 则直接将元素插入进去即可
2.桶位有元素但是没有形成链表(只有一个元素),此时需要将元素与要插入的元素key进行比较,如果相同则直接替换value,并且返回oldvalue
3.桶位元素形成链表,遍历链表,对每一个节点,都进行key的比较,如果相同则发生和2相同的情况,如果不同则将元素插入链表尾结点,并且进行判断,链表长度是否达到8,如果达到8则需要进行树化操作
4.桶位链表已经转化为红黑树,红黑树的插入操作,先找父节点,第一种是一直向下探测,直到找到左子树或者右子树为空,说明整个树中没有发现key相同的节点,此时节点就是插入节点的父节点,将当前节点插入到父节点的左子树或者右子树,根据插入节点的hash大小判断是左子树还是右子树,插入后有可能会打破平衡,需要平衡算法。第二种是,探测过程中找到key相同的节点,则直接返回该节点,然后进行替换操作。
红黑树特点
1.根节点为黑
2.叶子节点为黑