1.7和1.8的区别?
组成:1.7数组+链表 1.8数组+(链表|红黑树)
为什么要用红黑树,为什么不一上来就树化,树化的阈值为何是8,何时会树化,何时会退化为链表?
1.红黑树的作用是避免DoS攻击,防止因为链表过长影响性能,树化是偶然情况
1.1 hash表的查找,更新的时间复杂度O(1),而红黑树的查找,更新的时间复杂度是O(log2 n),TreeNode比普通的node大,如非必要,尽量使用链表
1.2 hash的值如果足够随机,则在hsah表内按泊松分布,在负载因子0.75情况下,长度超过8的链表出现的概率是亿分之六,选择8就是让树化的概率足够小
2.树化的两个条件:链表长度超过树化阈值8,数组容量大于等于64
3.退化情况1:在扩容时如果拆分树时,树元素个数小于等于六,则会退化链表
4.退化情况2:remove树节点时,如果节点,节点右孩子,节点左孩子,节点左孙子有一个为null,则会退化为链表。(注意是移除时)
索引如何计算:hashCode都有了,为何还要提供hash()方法(进行二次哈希值的计算)?数组容量为何是2的n次幂?
(按位与运算实际上就是把数转换为二进制数进行计算0 & 0= 0 ,0 & 1= 0,1 & 0= 0, 1 & 1= 1)
1.计算对象的hashCode(),再进行调用hashMap的hash()方法进行二次哈希,最后按位与数组容量减一得到索引
2.二次哈希hash()是为了综合高位数据,让哈希分布更加均匀
3.计算索引时,如果时2的n次幂可以使用位与运算代替取模,效率更高,扩容时hash&oldCap(旧的数组容量)==0的元素留在原来的位置,否则新位置=旧位置+oldCap
4.1.2.3都是为了配合容量为2的n次幂的优化手段,例如Hashtable的容量就不是2的n次幂,并不能说那种设计模式更优,设计者综合多种因素,最终选择了2的n次幂作为容量
介绍一下put方法流程,1.7和1.8的区别
1.Hashmap是懒惰创建数组的,首次使用才能创建数组
2.计算索引(桶下标)
3.如果桶下标还没人占用,创建Node占位返回
4.如果桶下标已经有人占用
1.已经是TreeNode走红黑树的添加或更新逻辑,如果链表长度超过树化阈值,走树化逻辑
2.是普通的Node,走链表的添加和更新的逻辑,如果链表长度超过树化阈值,走树化逻辑
5.返回前检查是否超过阈值,一旦超过进行扩容
不同之处:
1.链表插入节点时,1.7是头插法,1.8是尾插法
2. 1.7是大于等于阈值且没有空位时才扩容,1.8是大于阈值就扩容
3. 1.8在扩容计算Node索引时,会优化
加载因子为何是默认0.75f?
1.在空间占用和查询时间之间取得更好的权衡
2.大于这个值,空间节省了,但是链表就会比较长影响性能
3.小于这个值,冲突减少了,但扩容就会更频繁,空间占用多,造成浪费
多线程下会有什么问题?
1.1.7会出现扩容死链(因为1.7是头插法,会出现死链)
2.数据错乱(1.7,1.8,都会出现,因为多个线程同时进行,桶下标可能相同,同时进行可能会把先进行那个线程的数据进行覆盖,造成数据丢失)
key能否为null?作为key的对象有什么要求?
1.hashMap的key可以为null,但map的其他实现却不是
2.作为key的对象,必须实现hashCode和equals,并且key的内容不能被修改(不可变)
String对象的hashcode()如何设计的,为啥每次都乘以31?
目标是达到比较均匀的散列效果,每个字符串的hashcode足够独特