1,HashMap底层存储结构
HashMap在Jdk1.7的时候采用的是数组加链表的数据结构,jdk1.8之后采用了数组加链表加红黑树的数据结构。观察源码可知HashMap类中有一个非常重要的字段就是Node[] table,即哈希桶数组。而Node是HashMap的一个内部类,实现了Map.Entry接口,本身就是一个键值对。
2,解决Hash冲突的方法,HashMap采用了什么方法解决Hash冲突?
HashMap使用哈希表来存储数据的,当然哈希表不可避免的就会遇到hash冲突问题,解决hash冲突的方法大致有两种:1,开放地址法。2,链地址法。
1,开放地址法:当地址发生冲突时,按着某种方法继续探测哈希表中的其他存储单元,直到找到空位置为止。
2,链地址法:链地址法就是数组加链表的结合,在每一个数组元素上都有一个链表结构,当地址发生冲突时就讲数据存放在链表中。
而HashMap就是采用链地址法进行解决hash冲突的。
3,jdk1.8的HashMap中的链表达到多少个时会生成红黑树?
HashMap用链地址法解决hash冲突,则当链表里的长度太长就会严重影响HashMap的性能。于是在jdk1.8里,对数据结构做了进一步优化,引入了红黑树,当链表长度大于8的时候,链表就会转成红黑树,利用红黑树快速增删改查的特点提高HashMap的性能,其中会用到红黑树的插入、删除、查找等算法。
3,HashMap初始值的大小和负载因子的大小?
hashMap初始长度就是16,负载因子是0.75。HashMap所容纳的最大数据量为:长度*负载因子。即当长度达到这个值的时候就会发生扩容。
4,HashMap扩容机制
扩容(resize)就是重新计算容量,向HashMap对象里不停的添加元素,而HashMap对象内部的数组无法装载更多的元素时,对象就需要扩大数组的长度,以便能装入更多的元素。当然Java里的数组是无法自动扩容的,方法是使用一个新的数组代替已有的容量小的数组,就像我们用一个小桶装水,如果想装更多的水,就得换大水桶。底层是resize方法中的transfer方法将原有的Entry数组的元素拷贝到新的Entry数组里,扩容都是以2的N次幂进行扩容 一般是2倍。
5,HashMap线程安全问题 HashTable ConcurrentHashMap
HashMap是线程不安全的,多个线程同时写HashMap可能会导致数据的不一致。如果需要满足线程安全可以用ConcurrentHashMap,还有一个HashTable。但是HashTable是继承自Dictionary类,HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下,HashTable的效率非常低下,ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成,采用segment分段锁来保证线程安全。
HashTable无论key或value都不能为null,HashMap只能允许一个key为null,可以运行多个value为null。而且HashTable是线程安全的,HashMap是线程不安全的。
6,HashMap链表成环
由于HashMap线程不安全的,至于为何不安全,什么时候会出现问题,这里来讨论一下:
当有多个线程共同操作hashMap的put方法时,这个时候hashMap容量不够了,两个线程都去扩容执行resize方法,在这个时候cpu切换资源的话,会造成链表成环问题,死循环问题。
到此这篇关于HashMap面试题汇总(附答案)的文章就介绍到这了,更多相关HashMap面试题内容请搜索脚本之家以前的文章或继续浏览下面的相关文章,希望大家以后多多支持脚本之家!