java理论知识汇总-容器篇

容器思维导图

map的KV应避免为null

主要map类集合.png

HashMap的底层结构/HashMap如何解决哈希冲突

HashMap采用链地址法处理哈希冲突,它由数组+链表组成,底层结构是一个数组,而数组的元素是一个单向链表,当哈希冲突时,会将新值接入链表尾部

HashMap重写equals时,为什么要重写hashCode

HashMap用hashcode方法定位桶的位置,equals方法判断是否重复。如果重写了equals而没有重写hashcode方法,就会出现equals返回ture,但是hashcode不相同的对象,分处两个不同的桶,即在HashMap看来是两个不同的对象,显然不符合equals方法的定位。

HashMap如何定位桶的位置

  • 处理hash,将HashCode()的高16位和低16位异或
  • 对桶个数取模,index = h&(length-1),因为length总是2的n次方,所以等价于对length取模。

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

保证(length-1)二进制低位都为1,这样做有三点好处

  • 减少哈希冲突,例如01010101和01010111 与01111101作&运算得到的都是01010101。
  • 减少扩容时的开销,扩容前和扩容后的(length-1)二进制只在最高位(最左边的1)有差异,当最高位hash为0时,下标不变;为1时,新下标=旧下标+旧容量。
  • 性能考虑,当length总是2的n次方时,对length取模可用&运算优化,具有更高的效率

HashMap在jdk8优化了哪些

  • 优化了hash,将HashCode()的高16位和低16位异或,这么做可以在数组比较小的时候,也能保证考虑到高低位都参与到数组槽的计算中去。有效解决低位与操作碰撞的问题。
  • 链表超过8时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能
  • 优化了扩容后的下标重排的算法,当最高位hash为0时,下标不变;为1时,新下标=旧下标+旧容量。
  • 延迟初始化,第一次put数据时才进行HashMap的初始化。

HashMap多线程使用会导致死循环的原因

企业微信截图_b5ee2bcb-775a-4b65-ae96-e98c095e274a.png
  • 无论哪个版本的hashMap都是采用头插法将元素迁移到新数组中。头插法会将链表的顺序翻转,这也是形成死循环的关键点

HashMap添加数据的过程

  1. 第一次使用时初始化容器
  2. 计算元素所要储存的位置index
  3. 判断该链是否为红黑树并添加元素
  4. 超过容量阀值,扩容

HashMap什么时候扩容

当数据量达到容量*负载因子时发生扩容。

HashMap和LinkedHashMap的区别

  • HashMap无序,LinkedhashMap有序
  • LinkedhashMap是由HashMap+双向链结构组成的,内部类Entry多了before和after两个属性分别用来存储前置和后置节点。

ConcurrentHashMap核心

  • sizeCtl 标示位,同时也是扩容阀值
  • ForwardingNode 扩容时表示已经处理过的占位符
  • CAS+Synchronize确保并发安全
  • 如何统计元素的个数?counterCells
  • 扩容过程
  • put流程
  • 触发扩容的时机

ConcurrentHashMap触发扩容的时机

  • 槽数小于64且存在某一个槽位链表个数大于等于8时触发
  • 第一次put时扩容
  • 超出扩容阀值(sizeCtl)时

ConcurrentHashMap put流程

  • 第一次put初始化table
  • 通过处理过的hash确定槽位
  • 槽位没数据时,将数据CAS方式放在槽位上
  • 槽位正在扩容处理时,协助扩容
  • 槽位上有数据时,获取链表头锁(Synchronize),并将数据插入链表尾部(重复数据情况略)
  • 该槽位数据量大于等于8时,扩容或者转红黑树
  • 记录槽位数据量
  • 判断是否需要扩容

ConcurrentHashMap扩容流程(不考虑红黑树)

  • 创建新容器nextTable
  • 遍历旧容器所有槽位
  • 如果槽位上没有数据,槽位标记为已经处理
  • 如果槽位已经处理,跳过
  • 获取槽位链表头锁,同HashMap拆成两个链表分别放入nextTable对应槽位中
  • 原槽位标记为已经处理
  • 遍历完后,将新容器替换旧容器,设置新阀值

ConcurrentHashMap是如何统计元素的个数

  • 每当线程 put 一个元素到容器中,先尝试CAS算法累加到 BASECOUNT 上,如果失败,线程会被映射到一个 CounterCell 对象上采用 CAS 算法自旋地对其属性value进行加 1 操作,
  • 统计时,BASECOUNT +所有CounterCell的value
  • 元素个数是热点数据,这么做是为了减少cas自旋的次数

使用@sun.misc.Contended修饰CounterCell,消除伪共享带来的性能开销

  • @sun.misc.Contended的作用:使得被修饰对象独立占用一个缓冲行。
  • 使用场景:主要适用于频繁写的共享数据上
  • 什么是伪共享: 每个CPU 读取数据后都会存到自己的缓存里,为了节省空间,一个缓存行可能存储着多个变量,即伪共享
  • 伪共享带来的性能开销:CPU修改缓存A类数据时,会导致同一缓存行的B类数据也失效,倘若A类数据写操作频繁,B类数据读操作频繁,CPU需要经常从内存中重新读取B类数据。

ConcurrentSkipListMap

  • 跳表是可以实现二分查找的有序链表;
  • 每个元素插入时随机生成它的level(怎么生成的?)
  • 最低层包含所有的元素;
  • 如果一个元素出现在level(x),那么它肯定出现在x以下的level中;
  • 每个索引节点包含两个指针,一个向下,一个向右;
  • 跳表查询、插入、删除的时间复杂度为O(log n),与平衡二叉树接近;

ConcurrentSkipListMap put流程

  • 查找key的前置节点A
  • 如果A下一个节点B的key与要加入的key相等,则cas修改B的值
  • 创建新的节点,cas方式在A与B之间加入一个新节点
  • 随机生成索引level
  • 在小于level的索引中查找合适位置加入索引节点
  • 如果level大于当前最高索引level,则创建新的索引

Map比较

容器 数据结构 key能否为空 value能否为空 并发安全 如何实现并发安全 有序
HashMap 数组+链表/红黑树 允许 允许 -
LinkedHashMap HashMap+双向链表 允许 允许 - 只支持插入顺序和访问顺序
TreeMap 红黑树 不允许 允许 -
ConcurrentHashMap 数组+链表/红黑树 不允许 不允许 CAS + Synchronize
ConcurrentSkipListMap 跳表 不允许 不允许 CAS

为什么ConcurrentHashMap不允许key为null,而HashMap允许

  • HashMap对key为空的情况做了特殊处理,当key为空时返回的hash为0
  • 其他容器并没有这么做

为什么ConcurrentHashMap不允许value为null,而HashMap允许

  • value允许为空存在二义性,1 指定key并未在容器中映射过;2 put操作时value就为空。
  • 单线程情况下,可以通过containKey方法区分,因此HashMap等非并非安全的容器允许,多线程情况则不行,因此Concurrent系容器都不允许
  • value为null在Concurrent系代码实现中存在特殊含义,例如ConcurrentSkipListMap中表示数据正在被删除。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,546评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,224评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,911评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,737评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,753评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,598评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,338评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,249评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,696评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,888评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,013评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,731评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,348评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,929评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,048评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,203评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,960评论 2 355

推荐阅读更多精彩内容