java容器之Map

本文深入探讨了Java中的Map容器,包括HashMap、Hashtable、ConcurrentHashMap、TreeMap和LinkedHashMap的特点及应用场景。特别关注了HashMap和ConcurrentHashMap的实现原理。

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

java容器(二):Map

Collection 和 Map是java容器里面的俩大中流砥柱,在我的上一篇博客中,已经介绍过了Collection,那么今天我们再来聊聊Map里的好东西。

No picture you say a JB!啥也不先说,先上张图瞧瞧(度娘的)
在这里插入图片描述
这张图感觉好像有点问题,LinkedHashMap应该是HashMap的子类。。。,再就是少了一个我们今天要讲的ConcurrentHashMap

一. HashMap

  • 一个我们平常使用最最频繁的Map容器,它的查询速度是最快的,HashMap 根据键的 hashCode 值存储数据,大多数情况下可以直接定位到它的值,因而具有很快的访问速度,但遍历顺序却是不确定的。 HashMap 最多只允许一条记录的键为 null,允许多条记录的值为 null。HashMap 非线程安全,即任一时刻可以有多个线程同时写 HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用 Collections 的 synchronizedMap 方法使HashMap 具有线程安全的能力,或者使用 ConcurrentHashMap
  • HashMap的实现原理在面试中经常被问到,我的第一次电话面试也是被问到了,当时我是一点都没答上来,尴尬得一匹。。。,HashMap在jdk1.7和jdk1.8中实现原理都不一样,这边我们也来介绍一下
    • jdk1.7中,基于数组和链表实现,如下图所示:
      jdk1.7HashMap结构
    • 大方向上,HashMap 里面是一个数组,然后数组中每个元素是一个单向链表。上图中,每个绿色的实体是嵌套类 Entry 的实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next。
      1.capacity:当前数组容量,初始值16,始终保持 2^n(可以思考一下这里为什么是2的n次幂),可以扩容,扩容后数组大小为当前的 2 倍。
      2.loadFactor:负载因子,默认为 0.75。
      3.threshold:扩容的阈值,等于 capacity * loadFactor

  • jdk1.8中加入了红黑树,当链表的长度超过8以后,改用红黑树,因为链表遍历的时间是o(n),而红黑树是o(logn),结构如下图:
    在这里插入图片描述
    • 这里其实关于HashMap讲得还太少,比如说如何根据在散列表中找到对应的key值、为啥数容量保持2^n次幂。。。主要是我个人自己也还不是太懂,讲得比较粗糙,有兴趣的建议大家看看《Java编程思想》关于集合的知识,或者还可以自己看看HashMap的源码

二、Hashtable

  • Hashtable 是遗留类,已经过时了的,很多映射的常用功能与 HashMap 类似,不同的是它承自 Dictionary 类,并且是线程安全的,任一时间只有一个线程能写 Hashtable,但是并发性不如 ConcurrentHashMap,因为 ConcurrentHashMap 引入了分段锁。Hashtable 不建议在新代码中使用,不需要线程安全的场合可以用 HashMap 替换,需要线程安全的场合可以用 ConcurrentHashMap 替换。

三、ConcurrentHashMap

  • Concurrent意思是同时发生的、并发的,顾名思义它是为了解决HashMap的线程不安全问题,实现原理和HashMap大致相似,但是由于是可以并发的,所以在一些方面实现比较复杂。
  • 整个 ConcurrentHashMap 由一个个 Segment 组成,Segment 代表”部分“或”一段“的意思,所以很多地方都会将其描述为分段锁。也有些博文喜欢用“”来代表一个segment。简单理解就是,ConcurrentHashMap 是一个 Segment 数组,Segment 通过继承ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。
    在这里插入图片描述
  • 上图是concurrentHashMap的一个结构图,基于jdk1.7的,暂未引入红黑树。concurrencyLevel:并行级别、并发数、Segment 数,怎么翻译不重要,理解它。默认是 16,也就是说 ConcurrentHashMap 有 16 个 Segments,所以理论上,这个时候,最多可以同时支持 16 个线程并发写,只要它们的操作分别分布在不同的 Segment 上。这个值可以在初始化的时候设置为其他值,但是一旦初始化以后,它是不可以扩容的。再具体到每个 Segment 内部,其实每个 Segment 很像之前介绍的 HashMap,不过它要保证线程安全,所以处理起来要麻烦些。

    四、TreeMap
  • TreeMap 实现 SortedMap 接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用 Iterator 遍历 TreeMap 时,得到的记录是排过序的。如果使用排序的映射,建议使用 TreeMap。
    在使用 TreeMap 时,key 必须实现 Comparable 接口或者在构造 TreeMap 传入自定义的Comparator,否则会在运行时抛出 java.lang.ClassCastException 类型的异常。

    五、LinkedHashMap
  • LinkedHashMap 是 HashMap 的一个子类保存了记录的插入顺序,在用 Iterator 遍历LinkedHashMap 时,先得到的记录肯定是先插入的,也可以在构造时带参数,按照访问次序排序。由于在添加key-value时还要额外维护链表操作,性能上比HashMap要差(HashMap是最强的,速度最快,没有别的额外需求,首先使用它)

总结:

其实关于这些Map的知识还很多,一篇短博客讲不了那么多,再给大家推荐点干货吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值