面试知识点总结之集合框架TreeSet/TreeMap/ConcurrentHashMap/...

本文详细介绍了TreeSet的数据结构特点及其实现原理,包括其排序方式与内部实现机制。同时,深入探讨了ConcurrentHashMap的工作原理,包括其在JDK1.8中的改进,如采用CAS+synchronized实现的细粒度锁机制,以及put和get方法的具体流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TreeSet:

首先 set是可以去重的,tree是实现排序的,总结:

TreeSet是一个包含有序的且没有重复元素的集合,通过TreeMap实现

它继承了AbstractSet抽象类,实现了NavigableSet<E>,Cloneable,Serializable接口。
TreeSet是基于TreeMap实现的,TreeSet的元素支持2种排序方式:自然排序或者根据提供的Comparator进行排序。
 
TreeMap:
TreeMap可以比较元素大小,会对传入的key进行大小排序。自然顺序,自定义的比较器来进行排序;
数据结构:红黑树
TreeMap的基本操作 containsKey、get、put 和 remove 的时间复杂度是 log(n) 。 
 
ConcurrentHashMap:
 JDK1.7 和 JDK1.8 的实现方式不同
这里介绍JDK1.8
Node数组+链表+红黑树结构;在锁的实现上,抛弃了原有的 Segment 分段锁,采用CAS + synchronized实现更加细粒度的锁。
将锁的级别控制在了更细粒度的哈希桶数组元素级别,也就是说只需要锁住这个链表头节点(红黑树的根节点),就不会影响其他的哈希桶数组元素的读写,大大提高了并发度。
JDK1.8 中为什么使用内置锁 synchronized替换 可重入锁 ReentrantLock?
在 JDK1.6 中,对 synchronized 锁的实现引入了大量的优化,并且 synchronized 有多种锁状态,会从无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁一步步转换。
减少内存开销 。假设使用可重入锁来获得同步支持,那么每个节点都需要通过继承 AQS 来获得同步支持。但并不是每个节点都需要获得同步支持的,只有链表的头节点(红黑树的根节点)需要同步,这无疑带来了巨大内存浪费。
如何put

根据 key 计算出 hash 值;
判断是否需要进行初始化;
定位到 Node,拿到首节点 f,判断首节点 f:
如果为 null ,则通过 CAS 的方式尝试添加;
如果为 f.hash = MOVED = -1 ,说明其他线程在扩容,参与一起扩容;
如果都不满足 ,synchronized 锁住 f 节点,判断是链表还是红黑树,遍历插入;
4. 当在链表长度达到 8 的时候,数组扩容或者将链表转换为红黑树。

get

根据 key 计算出 hash 值,判断数组是否为空;
如果是首节点,就直接返回;
如果是红黑树结构,就从红黑树里面查询;
如果是链表结构,循环遍历判断。
get 方法不需要加锁。因为 Node 的元素 value 和指针 next 是用 volatile 修饰的,在多线程环境下线程A修改节点的 value 或者新增节点的时候是对线程B可见的。

ConcurrentHashMap 不支持 key 或者 value 为 null 的原因?

我们先来说value 为什么不能为 null。因为 ConcurrentHashMap 是用于多线程的 ,如果ConcurrentHashMap.get(key)得到了 null ,这就无法判断,是映射的value是 null ,还是没有找到对应的key而为 null ,就有了二义性。

而用于单线程状态的 HashMap 却可以用containsKey(key) 去判断到底是否包含了这个 null 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大富大贵7

很高兴能够帮助到你 感谢打赏

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

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

打赏作者

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

抵扣说明:

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

余额充值