1.Map類集合
Map是和Collection同級(jí)別的接口,但是Map也可以返回一些collection類型的,比如keySet()和value()返回所有key的視圖和所有value的視圖。
0.健值對(duì)是否允許空
HashTable key和value 都不允許為null
ConcurrentHashMap key和value 都不允許為null
TreeMap key不允許為null,value允許
HashMap 都允許
所以,在你用hashMap轉(zhuǎn)換到conHAshMap的時(shí)候要注意key有沒有null。
1.TreeMap
TreeMap中的元素是有序且不重復(fù)的,但是,他的去重復(fù)和HashMap不同,TreeMap是通過實(shí)現(xiàn)comparator的compare方法或者compareable的compareTo()方法來實(shí)現(xiàn)的,如果比較吼認(rèn)為兩個(gè)不想等,就可以存放。而HashMap的去重是通過重寫equals和hashCode方法。
2.HashMap
以下基于jdk1.7
hashMap的坑:
1.put操作先把長度+1再做addEntry。
2.在createEntry方法中,當(dāng)算出來要添加的位置之后,不管原本的數(shù)據(jù)對(duì)應(yīng)下表元素是不是null,直接把新添加的元素放在表頭,即便原來是鏈表,也掛在添加元素的后面,這是元素丟失的原因之一。
3.resize擴(kuò)容操作,
4.transfer操作:把數(shù)據(jù)從舊表遷移到新的表,在遷移的過程中,舊表如果仍然可以進(jìn)行增加操作,如果增加到的slot是新表已經(jīng)遍歷過的,那就白添加了。當(dāng)resize完成,后續(xù)元素可以正常add了,遷移之后,resize線程會(huì)賦值給table線程的共享變量,從而覆蓋其他線程。這樣的話有的線程在add的時(shí)候就白add了。
2.1 HashMap丟失對(duì)象場(chǎng)景:
并發(fā)賦值時(shí)被覆蓋;擴(kuò)容時(shí)往已遍歷的舊map寫數(shù)據(jù)會(huì)丟失;并發(fā)擴(kuò)容的時(shí)候有的線程創(chuàng)建的新表會(huì)被覆蓋。
2.2 死循環(huán)
發(fā)生在擴(kuò)容的時(shí)候,假設(shè)現(xiàn)在有HashMap,長度為4,有三個(gè)節(jié)點(diǎn)都掛在同一個(gè)table[i]里面,現(xiàn)在有個(gè)線程A、B同時(shí)觸發(fā)了擴(kuò)容操作,假設(shè)三個(gè)節(jié)點(diǎn)rehash之后還是在同一個(gè)table[i],那么,就可能出現(xiàn)死循環(huán)。
3.2 1.7和1.8的比較
1.7中采用數(shù)組+鏈表的組合,1.8中采用數(shù)組+鏈表/紅黑樹,當(dāng)鏈表長度大于8就轉(zhuǎn)成紅黑樹。
put方法的不同
1.8對(duì)putVal方法添加元素的分析如下:
①如果定位到的數(shù)組位置沒有元素 就直接插入。
②如果定位到的數(shù)組位置有元素就和要插入的key比較,如果key相同就直接覆蓋,如果key不相同,就判斷p是否是一個(gè)樹節(jié)點(diǎn),如果是就調(diào)用e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value)將元素添加進(jìn)入。如果不是就遍歷鏈表插入(插入的是鏈表尾部)。
1.7對(duì)于put方法的分析如下:
①如果定位到的數(shù)組位置沒有元素 就直接插入。
②如果定位到的數(shù)組位置有元素,遍歷以這個(gè)元素為頭結(jié)點(diǎn)的鏈表,依次和插入的key比較,如果key相同就直接覆蓋,不同就采用頭插法插入元素。