Map

Map是Java里的一个接口,常见的实现类有HashMap、LinkedHashMap、TreeMap和ConcurrentHashMap

HashMap

HashMap底层数据结构是数组+链表/红黑树(JDK1.8后使用Node来存放键值对,Node实现Map.Entry接口)

  • 当我们new一个HashMap的时候会发生什么?

HashMap有几个构造方法,但最主要的就是指定初始size和负载因子的大小,如果我们不指定,默认HashMap的大小为16负载因子的大小为0.75。还有就是:HashMap的大小只能是2的整数次幂,比如你指定HashMap大小为10,实际上最终HashMap的大小是16,具体可以在tableSizeFor方法看到。

  • 元素是在HashMap中是如何存储的?(put和get方法的实现?)

当我们把元素放进HashMap的时候(put的时候),需要先算出这个元素(用Entry对象的key来算)的hash值,然后再根据hash值算出这个元素在hash表(数组)中的位置(HashTable是用hash值对size进行取模运算得到,HashMap是用hash值对size-1按位与运算得到,HashMap更加高效,所以HashMap的size只能是2的整数次幂就是为了方便位运算)。

当两个元素的hash值计算出在hash表同一个位置时,就会在hash表对应的位置使用尾插法(JDK1.8之前是头插)将元素插入链表,如果链表的长度超过了8在JDK1.8中会将链表结构转为红黑树结构(如果红黑树大小小于6则会自动退化为链表结构),因为链表长度过长的话查找效率就低(O(n)),而红黑树的插入和查找效率相对均衡(都是O(logn))。

当HashMap中存放元素的个数超过了size0.75后,则HashMap会进行扩容,每次扩容都扩为原来size的两倍*(能否把负载因子调高,减少扩容的次数?可以,但不建议,因为会增加hash冲突的概率,影响查找效率)。

在get的时候,还是对key做hash运算,计算出该key在hash表中的index,然后判断是否有hash冲突,如果没有hash冲突就直接返回数据,如果有hash冲突就再判断存储的数据结构是链表还是红黑树,按照对应的数据结构取出数据。

  • 在HashMap中怎么判断两个元素(key)是否相同?

首先会比较hash值,如果hash值不同,那这两个元素一定不同,如果hash值相同,则说明产生了hash冲突,接着用equals方法判断两个元素是否相同。

  • HashMap是线程安全的吗?

HashMap不是线程安全的。在多线程环境下,HashMap有可能会有数据丢失和获取不了最新数据的问题,比如:线程A put一条数据进去了,但是线程B get不到这条数据。

LinkedHashMap

LinkedHashMap底层数据结构是数组+链表+双向链表

  • LinkedHashMap的插入为什么是有序的?

LinkedHashMap实际上是继承了HashMap,在HashMap的基础上维护了一个双向链表。有了这个双向链表,我们的插入就可以是有序的。LinkedHashMap在遍历的时候实际用的是双向链表来遍历的,所以LinkedHashMap的大小不会影响遍历的性能。

TreeMap

TreeMap底层数据结构是红黑树,每个节点就是一个Entry对象。

  • TreeMap为什么是有序的?

TreeMap的有序是通过Comparator比较器来比较的(在TreeMap构造时传入Comparator比较器,如果没有传入比较器,则使用默认的比较器,即自然顺序)。TreeMap的key不能为null,因为null值无法比较大小。

ConcurrentHashMap

ConcurrentHashMap底层数据结构也是数组+链表/红黑树

  • 除了ConcurrentHashMap还有什么线程安全的Map?

还有HashTable和使用Collections来包装出一个线程安全的Map(Collections.synchronizedMap)。但是这两种线程安全Map效率比较低,是直接在HashMap的外层套synchronized,所以使用线程安全的Map一般用ConcurrentHashMap。

  • ConcurrentHashMap如何实现线程安全?

ConcurrentHashMap通过部分加锁和CAS算法来实现同步。部分加锁:在put操作时,对数组的每一个元素(数组中的元素是链表头或者是红黑树根)进行加锁(JDK1.7中是对某几个连续元素组成的segment进行加锁,JDK1.8缩小了锁的粒度,大大减小了锁的竞争),在hash完美不冲突的情况下最多可以支持n(n为数组大小)个线程同时put操作。

put操作:

  1. 首先判断数组是否为null,因为ConcurrentHashMap把数组的初始化推迟到了第一次put操作时,如果数组为null,则调用数组初始化方法。在调用初始化方法时,有一个sizeCtl属性,如果sizeCtl<0则表示有另外的线程在执行初始化操作,当前线程让出CPU时间片(Thread.yield())
  2. 当数组不为null,则判断此次put是否存在hash冲突,如果不存在hash冲突,则直接使用CAS的方式(判断此次插入的数组下标对应的CAS值,相等则直接插入数据,不相等更新该线程所得到的CAS值,并重新进行put操作)在数组中插入此次put的Node
  3. 在hash冲突的put之前,先检查是否有其他线程正在扩容,如果有,则调用helpTransfer方法帮助其他线程进行扩容
  4. 当此次put存在hash冲突时,synchronized锁住数组中的Node节点(即链表的表头或者红黑树的根),然后判断存储Node节点的数据结构,根据不同的数据结构进行遍历,遍历时先看是否链表或红黑树中有与此次put相同的key,如果有相同的key就修改其对应的value,如果没有就在链表尾部插入或红黑树的插入方式插入此次put的Node。如果是hash冲突的链表插入,在插入完成后会判断链表的长度是否达到临界值8,如果达到8,则会调用treeifyBin方法把链表转为红黑树。treeifyBin在执行前会先判断数组的长度是否小于64,若小于64则会优先做扩容操作,再把链表转为红黑树
  5. 当put操作结束后,调用addCount方法统计元素个数,并判断是否要进行扩容操作

get操作:get操作不加锁,通过CAS的方法取数组中对应的节点(如果CAS失败则更新CAS值再取一次)。ConcurrentHashMap支持边扩容边进行get操作。

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

推荐阅读更多精彩内容