你知道的越多,你不知道的越多
点赞再看,养成习惯
欢迎实名diss建议,希望我们一起有点东西
前言
相信hashMap对每个同行人来说,都不陌生,工作中都会有用到。
知识体系的广度决定你能走多远,而深度决定你能走多高。
如果你不了解底层原理!如果你想面试大厂!如果你想深度学习!
请往下看:
HashMap的历史演变:
-
在Java7的时候hashMap的底层采用的是数组+链表实现的。
-
java8则采用的是数组+链表+红黑树实现。
从而引入一个BATJ的面试问题,hashMap的实现原理?
hashMap源码里有两个很重要的属性,一个是初始容量(默认16),一个是负载因子(默认0.75),
我们在new一个hashMap对象的时候,实际上底层会默认分配一个大小为16的静态数组arr,我们调用put(key,value)方法的时候,会使用^(异或)运算计算出key的hash,然后通过hash与arr-1进行与运算(hash & (arr.length-1))得到要放入的数组的下标index,如果arr[index]!=null,此时,单向链表就出现了,java7采用的是头插法,java8采用的是尾插法。
什么是头插法?什么是位插法?
java7会采用头插法插入链表中,而java8采用的是尾插法,在并发的时候,多个线程同时调用put方法,头插法则会出现环形链表,造成程序死循环,从而导致堆空间内存溢出,抛出异常。
所谓头插法,简单的说其实就是后面插入的结点作为链表的头结点。尾插法则是后面插入的元素作为链表的尾部结点。
对于hashMap而言,当数据量达到负载容量,会进行扩容操作,而头插法则会导致新的链表和旧的链表是反向的
hashMap的扩容机制
因为hashMap的数组容量是有限的,所以不可避免的存在扩容机制,也就是resize()方法。
当数组中已有数据结点的容量>=初始容量*负载因子的时候,hashMap会自动扩容。
默认是将数组的容量扩大为原来的两倍,遍历原来的数组,重新进行hash运算,然后通过得到的新的hash,插入到新的数组中。
HashMap在JDK1.8及以后的版本中引入了红黑树结构,若桶中链表元素个数大于等于8时,链表转换成树结构;
若桶中链表元素个数小于等于6时,树结构还原成链表。
什么是hash碰撞呢?
所谓hash碰撞,其实就是通过hash算法计算出的hash值相等
hash碰撞有什么坏处?
就HashMap而言,如果发生hash碰撞的次数过多,在java7的时候会导致某一条单链表上数据量过多,
导致查询的效率很低,java8的时候其实情况相对来说好很多,
因为使用了红黑树,二分法查询的效率会快很多,不管怎样,多多少少还是会降低查询的效率。
hashMap的作者为什么选择16作为初始容量?为什么选择0.75作为负载因子?
假设h=0000 0001
如果初始容量为15, length-1=14, 转换成二进制为:0000 1110
则 index = 0000 0000
如果初始容量为16, length-1=15, 转换成二进制为:0000 1111
则 index = 0000 0001
结论:
为了减少hash碰撞,我们希望hash均匀分布,即hashCode(key)均匀分布,当length为2的等幂次方的时候,转换成二进制之后后几位都是1,这样就可以让index的结果取决于hashcode了,只要输入的hashCode本身就是均匀的,index就是均匀的
根据泊松分布,在负载因子默认为0.75的时候,单个hash槽内元素个数为8的概率小于百万分之一,所以将7作为一个分水岭,等于7的时候不转换,大于等于8的时候才进行转换,小于等于6的时候就化为链表。
hashMap是线程安全的吗?如果不是,多线程情况下有何解决方案?
通过源码,我们可以知道,hashMap没有任何锁的实现,所以hashMap不是线程安全的。如果想要实现线程安全,有以下解决方案:
1.hashTable替代hashMap,hashTable的底层是有加syschronized关键字的,即上锁,只允许单个线程访问。(效率较低)
2.采用collections.syschronizedHashMap(),(效率较高)
hashMap为什么要重写equals()方法?
因为在java中,所有的对象都是继承于Object类。Ojbect类中有两个方法equals、hashCode,这两个方法都是用来比较两个对象是否相等的。 在未重写equals方法我们是继承了equals方法,那里的 equals是比较两个对象的内存地址,显然我们new了2个对象内存地址肯定不一样。 然而hashMap通过比较key是否相等,是通过hash值+key去比较的,所以,需要去重写equals。
能否手写hashMap()?能否手写红黑树?(对!你没看错!手写红黑树!)
由于近期时间的原因,实在抽不出时间写了,网上有很多资料,包括AVL树,红黑树,b树,b+树,b*树... 下一篇文章会一一实现各种树。
总结
在大厂面试中,HashMap的底层绝对是要烂熟于心的
通过一个hashMap,我们可以衍生出hashTable,TreeMap,concurrentHashMap等等一系列底层实现。
知其然且知其所以然,让我们一起有点东西。