HashMap的底层数据结构?
数组+链表+红黑树,默认容量=16(无参构造为0,但put首次会给16),负载因子=0.75,默认容量阀值=容量*负载因子=12,链表长度阀值(引入红黑树)=8----(7是比较值,6的话就会去掉红黑树)
HashMap的存取原理?(冲突有两种,一直是本身key已存在)
1)存,
容量为0时,调用resize()初始化默认容量=16。
拿key值对数组长度hash取hashcode,来决定存储的数组索引位置,无冲突直接入数组。若有冲突,先判断Key本身是否重复了,如果重复了,覆盖旧的。如果不是这种冲突,则再判断,当前尾入口是红黑树的话,直接插入;如果不是这种冲突,再判断,是否要引入红黑树(链表长度阀值>8,引入红黑树,8时不加不减),不引入就插入链表。最后,,需要考虑下是否扩容(如果超过负载因子*容量的值,扩容向左位运算1位,重新hash所有值)
巧记:容量16,取hashcode,无冲突直接入数组;有冲突,Key重复就覆盖,其他入链表或红黑树。是否扩容
2)取
首先根据key的Hash值进行数组方面的寻找,数组没找到直接返回null,若找到这个数组之后,判断key是不是唯一(有无链表红黑树),如果key唯一(链表长度0),则直接返回,如果不唯一(链表长度>0),则遍历链表比较每个key,如果还没找到且存在红黑树,再直接调用getTreeNode(),使用获取对应key的data
巧记:先找到这个数组索引,若key不唯一,使用equals进行Hash值遍历判断
Java7和Java8的区别?
JDK 1.8 的优化目的主要是:减少 Hash冲突 & 提高哈希表的存、取效率:
1、java8之后链表有红黑树(log(n)效率快,原n)
2、表插法遇到多线程扩容时可能产生---环形链死循环。
-----------多线程高并发同时扩容,前一个改了next的指向,影响后一个扩容的使用,造成环形链死循环。
为啥会线程不安全?
到put/get方法都没有加同步锁,多线程情况最容易出现的就是:无法保证上一秒put的值,下一秒get的时候还是原值,所以线程安全还是无法保证。
有什么线程安全的类代替么?
一般都会使用HashTable或者ConcurrentHashMap
有什么线程安全的类代替么?
一般都会使用HashTable或者ConcurrentHashMap
HashTable我看过他的源码,很简单粗暴,直接在方法上锁sychronized,锁的有点过分了;
ConcurrentHashMap我看过他的源码,销威好点,直接在方法里面的代码块上锁sychronized;
默认初始化大小是多少?为啥是这么多,都是2的幂?
答一:默认给16..
答一:因为官方要求是2的次方,方便对key进hash运算取hashCode值时,做位运算,比普通运算快很多。要求是2的次方,16只是作者觉得合适。
总结: 1 减少hash碰撞
2 提高map查询效率
3 分配过小防止频繁扩容
4 分配过大浪费资源
HashMap的扩容方式?负载因子是多少?为什么是这么多?
扩容是对数组长度扩容,和链表没关系,只是链表上的数据要重新hash,可能不是一个链表上了(2个key也可能产生链表)
1、向左位运算一位(*2)。初始若用无参构造函数会默认给16.因为官方要求是2的次方,方便对key进hash运算取hashCode值时,做位运算,比普通运算快很多。要求是2的次方,16只是作者觉得合适。
这个1.7是直接*2,但1.8是当前容量12+16*n.
2、另外,这个扩容实际上,是要做数据Hash值扩容重排的:
jdk1.7扩容是重新计算hash;jdk1.8是要看看原来的hash值新增的那个bit是1还是0好了,如果是0则索引没变,如果是1则索引变成"原索引+oldCap".这是jdk1.8的亮点,设计的确实非常的巧妙,即省去了重新计算hash值得时间,又均匀的把之前的冲突的节点分散到新的数组bucket上
jdk1.7在rehash的时候,旧链表迁移到新链表的时候,如果在新表的数组索引位置相同,则链表元素会倒置,但是jdk1.8不会倒置
为什么链表的长度为8是变成红黑树?为什么为6时又变成链表?
HashMap中初始化大小为什么是16? 为什么链表的长度为8是变成红黑树?为什么为6时又变成链表?_坑里水库的博客-CSDN博客
解决树高度问题,提升查询是效率
HashMap的主要参数都有哪些?
容量、负载因子,table就是node类型的数组,里面放的数组元素可理解为链表形。
HashMap是怎么处理hash碰撞的?
一般引入链表来解决。不同的值hashcode相同时,会放入链表后面,1.8用尾插法,如果链表长度超过8还会放入红黑树(log(n)效率快)
hash的计算规则?
对数组长度进行hashhash的为什么要重写hashcode?
Object类equals默认比较两个对象的内存地址,我们想把它重写成对象值的比较(因为要比较hashcode值是否冲突)。
而hashcode是本地方法,hashcode是根据对象的内存地址经哈希算法得来的,不同的对象的内存地址和原来Object类equals是一样道理,比较地址是不相同的,.hashcode就不同,我们要改写hashcode方法,也比较其对象值
比如,Student类的重写后的equals方法和hashcode方法,现在有两个Student对象:
Student s1=new Student("小明",18); Student s2=new Student("小明",18);此时s1.equals(s2),重写后一定返回true,不重写hashcode就会不一样。
不满足hashcode规定的规则:两个对象相等其哈希值一定相等