集合的重点

一、三大集合接口引出

1、集合分类

Java中的集合,从上层接口来看分为两类,Map(双列集合)和Collection(单列集合)。而Collection接口的子接口又包括了Set和List接口,从而形成了我们常见的三大集合Map、Set和List。
在这里插入图片描述

2、Java中常见的集合

Map接口和Collection接口是所有集合框架的父接口。

  • Map接口的实现类主要有:HashMap、TreeMap、Hashtable、LinkedHashMap、ConcurrentHashMap以及Properties等。
  • Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等。
  • List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等。

3、HashMap、HashTable、ConcurrentHashMap的区别?底层实现是什么?

(1)、区别
a、HashMap方法没有Synchronized修饰,线程不安全;HashTable线程安全,但效率低。
b、HashMap允许key和value为null,而HashTable不允许。
c、ConcurrentHashMap结合二者的优点,即安全又保证了效率。体现在ConcurrentHashMap锁的方式是稍微细粒度的,ConcurrentHashMap将hash表分为16个桶(默认值),诸如get、put、remove等常用操作只锁上当前需要用到的桶。个人理解:可以把每个桶看做是一个HashTable。
(2)、底层实现
A、HashMap和HashTable:jdk8之前是数组+链表实现,jdk8开始链表高度到8、数组长度超过64,链表转为红黑树,元素以内部类Node节点存在。数组存放的是HashCode、链表/红黑树存放数据HashEntry(键值对),时间复杂度由O(n)变为O(logN)。结构如下图(注:HashTable的不同在于get、put等方法用Synchronized修饰起来):
在这里插入图片描述

B、ConcurrentHashMap:

参考:https://blog.csdn.net/weixin_43185598/article/details/87938882
https://blog.csdn.net/qq_41884976/article/details/89532816

jdk8之前:数组(Segment)+数组(HashEntry)+链表(HashEntry节点),一个Segment中包含一个HashEntry数组,每个HashEntry又是一个链表结构。也可以理解Segment是一种可重入的锁ReentrantLock,每个Segment守护一个HashEntry数组里的元素,对数据进行操作时,必须先获取对应的Segment锁。
将hash表分为16个桶(相当于每个桶放了一个HashTable),诸如get、put、remove等常用操作只锁上当前需要用到的桶。
在这里插入图片描述
Jdk8:
A、Node数组+链表/红黑树(类似hashMap<jdk1.8>),将原先Table数组+单向链表的数据结构,变更为table数组+单向链表+红黑树的结构(记住链表变红黑树的时间复杂度变化)。
B、取消segment字段,直接采用table数组元素作为锁,从而实现了对每一行数据进行加锁,并发控制使用Synchronized和CAS来操作。
在这里插入图片描述

4、为什么HashMap不安全?

(1)、在jdk1.7中,当并发执行扩容操作时会造成环形链(死循环)和数据丢失的情况。
(2)、在jdk1.8开始,在并发执行put操作时会发生数据覆盖的情况。
详细参考:https://blog.csdn.net/zzu_seu/article/details/106669757

5、HashMap的扩容步骤:

HashMap里面默认的负载因子大小为0.75,也就是说,当Map中的元素个数(包括数组,链表和红黑树中)超过了16*0.75 = 12 之后开始扩容。将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫做rehashing,因为它调用hash方法找到新的bucket位置。
当然了,上述的扩容机制是比较低效的。所以,我们伟大的JDK开发人员在1.8版本中做了一个扩容效率方面的优化。因为是2的N次幂扩容,所以一个元素要么在原位置不动,要么移动到当前位置+2的N次幂(也就是oldIndex+OldCap的位置)。

怎么实现呢?
a、说白了,就是通过新增的bit位置上是0还是1来判断。
b、0则是原位置,1则是oldIndex+OldCap位置。

这个设计确实非常的巧妙,既省去了重新计算hash值的时间,而且同时,由于新增的1bit是0还是1可以认为是随机的,因此resize的过程,均匀的把之前的冲突的节点分散到新的bucket了。这一块是JDK1.8新增的优化点,感兴趣的同学可以去看下源码。
但是,需要注意的是在多线程环境下,HashMap扩容可能会导致死循环。

6、解决Hash冲突的方法有哪些?

a、拉链法(HashMap使用的方法)
b、线性探测再散列法
c、二次探测再散列法
d、伪随机探测再散列法

7、ArrayList和LinkedList有哪些区别?

a、ArrayList底层使用了动态数组实现,实质上是一个动态数组。
b、LinkedList底层使用了双向链表实现,可当作堆栈、队列、双端队列使用。
c、ArrayList在随机存取方面效率高于LinkedList。
d、LinkedList在节点的增删方面效率高于ArrayList。
e、ArrayList必须预留一定的空间,当空间不足的时候,会进行扩容操作。
f、LinkedList的开销是必须存储节点的信息以及节点的指针信息。

8、List和Set的区别

a、List是有序的并且元素是可以重复的。
b、Set是无序(LinkedHashSet除外)的,并且元素是不可以重复的(此外的有序和无序是指放入顺序和取出顺序是否保持一致)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Senye_ing

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

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

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

打赏作者

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

抵扣说明:

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

余额充值