集合面试

描述一下Map put的过程

1、首次扩容:

先判断数组是否为空,若数组为空则进行第一次扩容(resize);

2、计算索引:

根据键值key计算hash,得到键值对在数组中的索引;

3、插入数据:

如果当前位置元素为空,则直接插入数据;

如果当前位置元素非空,且key已存在,则直接覆盖其value;

如果当前位置元素非空,且key不存在,则将数据链到链表末端;

若链表长度>8且当前数组长度>64时,则将链表转换成红黑树,并将数据插入树中;

4、再次扩容

插入成功后,如果数组中元素个数(size)超过threshold(数组长度*0.75),则再次进行扩容操作(扩容为原来的两倍)
链表长度>=8且当前数组长度小于64时,也会进行扩容。
在这里插入图片描述

介绍一下HashMap的扩容机制

在这里插入图片描述
补充:
如果是红黑树,则先遍历红黑树,先计算出红黑树中每个元素对应在新数组中的下标位置;
a:统计每个下标位置的元素个数
b:如果该位置下的元素个数超过了8,则生成一个新的红黑树,并将根节点添加到新数组的对应位置
c:如果该位置下的元素个数没有超过8,那么则生成一个链表,并将链表的头节点添加到新数组的对应位置
在这里插入图片描述

hashMap的寻址算法

在这里插入图片描述

在这里插入图片描述

为何HashMap的数组长度一定是2的次幂

1、计算索引时效率更高:如果是2的n次幂可以使用位与运算代替取模

97%16=116-1&97=1
99%32=332-1&99=3

2、扩容时重新计算索引效率更高:hash&oldCap == 0的元素留在原来的位置,否则新位置=旧位置 + oldCap

HashMap为什么用红黑树而不用B树?

B/B+树多用于外存上时,B/B+也被成为一个磁盘友好的数据结构。

HashMap本来是数组+链表的形式,链表由于其查找慢的特点,所以需要被查找效率更高的树结构来替换。如果用B/B+树的话,在数据量不是很多的情况下,数据都会“挤在”一个结点里面,这个时候遍历效率就退化成了链表。

说一说TreeSet和HashSet的区别

HashSet、TreeSet中的元素都是不能重复的,并且它们都是线程不安全的,二者的区别是:

  • HashSet中的元素可以是null,但TreeSet中的元素不能是null;

  • HashSet不能保证元素的排列顺序,而TreeSet支持自然排序、定制排序两种排序的方式;

  • HashSet底层是采用哈希表实现的,而TreeSet底层是采用红黑树实现的。

注意:HashSet是基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。它封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。

HashMap 是怎么解决哈希冲突的?

P63
要了解Hash冲突,那首先我们要先了解Hash算法和Hash表。

Hash 算法,就是把任意长度的输入,通过散列算法,变成固定长度的输出,这
个输出结果是散列值。

Hash 表又叫做“散列表”,它是通过key直接访问在内存存储位置的数据结构,
在具体实现上,我们通过hash函数把key映射到表中的某个位置,来获取这个
位置的数据,从而加快查找速度。

所谓hash冲突,是由于哈希算法被计算的数据是无限的,而计算后的结果范围
有限,所以总会存在不同的数据经过计算后得到的值相同,这就是哈希冲突。

①开放定址法,也称为线性探测法,就是从发生冲突的那个位置开始,按照一定的
次序从hash表中找到一个空闲的位置,然后把发生冲突的元素存入到这个空闲
位置中。ThreadLocal就用到了线性探测法来解决hash冲突的。
②链式寻址法,这是一种非常常见的方法,简单理解就是把存在hash冲突的key,以单向链表的方式来存储,比如HashMap就是采用链式寻址法来实现的。像这样一种情况,存在冲突的key直接以单向链表的方式进行存储。
③再hash法,就是当通过某个hash函数计算的key存在冲突时,再用另外一个hash 函数对这个key做hash,一直运算直到不再产生冲突。这种方式会增加计算时间,性能影响较大。

HashMap在JDK1.8版本中,通过链式寻址法+红黑树的方式来解决hash冲突问题,其中红黑树是为了优化Hash表链表过长导致时间复杂度增加的问题。当链表长度大于8并且hash表的容量大于64的时候,再向链表中添加元素就会触发转化。

HashMap补充:

hash(key)方法计算hash值
计算方法是键的hashCode()方法与高位16进行异或运算得到hash值
在这里插入图片描述
将元素存储在i = (n - 1) & hash的下标链表中
在这里插入图片描述

ArrayList和LinkedList有什么区别?

1、ArrayList的实现是基于数组,LinkedList的实现是基于双向链表;

2、对于随机访问ArrayList要优于LinkedList,ArrayList可以根据下标以O(1)时间复杂度对元素进行随机访问,而LinkedList的每一个元素都依靠地址指针和它后一个元素连接在一起,查找某个元素的时间复杂度是O(N);

3、对于插入和删除操作,LinkedList要优于ArrayList,因为当元素被添加到LinkedList任意位置的时候,不需要像ArrayList那样重新计算大小或者是更新索引;

4、LinkedList比ArrayList更占内存,因为LinkedList的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。

ArrayList的实现原理

1、ArrayList底层是用动态数组实现的
2、ArrayList初始容量为0,当第一次添加数据的时候才会初始化容量为10
3、ArrayList在进行扩容的时候是原来容量的1.5倍,每次扩容都需要拷贝数组(Arrays.copyOf 方法)
4、ArrayList在添加数据的时候

  • 确保数组已使用长度(size)+1后足够存下下一个数据
  • 计算数组容量,如果当前数组已经使用长度+1后大于当前数组长度,则调用grow方法进行扩容。
  • 确保新增的数据有地方存储之后,则将新元素添加到size位置上
  • 返回添加成功布尔值
    在这里插入图片描述
    在这里插入图片描述

ArrayList list = new ArrayList(10)扩容几次

声明和实例了一个ArrayList,指定了容量为10,未扩容。

如何实现数组和List之间的转换

在这里插入图片描述
在这里插入图片描述

说一说你对LinkedHashMap的理解

LinkedHashMap使用双向链表来维护key-value对的顺序(其实只需要考虑key的顺序),该链表负责维护Map的迭代顺序,迭代顺序与key-value对的插入顺序保持一致。

LinkedHashMap可以避免对HashMap、Hashtable里的key-value对进行排序(只要插入key-value对时保持顺序即可),同时又可避免使用TreeMap所增加的成本。

LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap的性能。但因为它以链表来维护内部顺序,所以在迭代访问Map里的全部元素时将有较好的性能。

如何得到一个线程安全的Map?

  • 使用Collections工具类,将线程不安全的Map包装成线程安全的Map;
  • 使用java.util.concurrent包下的Map,如ConcurrentHashMap;

HashMap与HashTable区别

  • 从功能特性的角度来说HashTable是线程安全的,而HashMap不是。
  • HashMap的性能要比HashTable更好,因为,HashTable采用了全局同步锁来保证安全性,对性能影响较大
  • 从内部实现的角度来说HashTable使用数组加链表、HashMap采用了数组+链表+红黑树。
  • HashMap初始容量是16、HashTable初始容量是11
  • HashMap 可以使用null作为key,HashMap会把null转化为0进行存储,而Hashtable 不允许。
  • 最后,他们两个的key的散列算法不同,HashTable直接是使用key的hashcode对数组长度做取模。而HashMap对key的hashcode做了二次散列,从而避免key的分布不均匀问题影响到查询性能。

说一说HashSet的底层结构

HashSet是基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。它封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

pk5515

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值