深入淺出講解Java集合之Map接口
一、Map接口繼承樹(shù)
Map:雙列數(shù)據(jù),存儲(chǔ)key-value
對(duì)的數(shù)據(jù) ---類(lèi)似于高中的函數(shù):y = f(x)
A.HashMap
:作為Map的主要實(shí)現(xiàn)類(lèi);線程不安全的,效率高;存儲(chǔ)null的key和value
a.LinkedHashMap
:保證在遍歷map元素時(shí),可以按照添加的順序?qū)崿F(xiàn)遍歷。
原因:在原有的HashMap
底層結(jié)構(gòu)基礎(chǔ)上,添加了一對(duì)指針,指向前一個(gè)和后一個(gè)元素。
對(duì)于頻繁的遍歷操作,此類(lèi)執(zhí)行效率高于HashMap
.
B.TreeMap
:保證按照添加的key-value
對(duì)進(jìn)行排序,實(shí)現(xiàn)排序遍歷。此時(shí)考慮key的自然排序或定制排序
底層使用紅黑樹(shù)
C.Hashtable
:作為古老的實(shí)現(xiàn)類(lèi):線程安全的,效率低;不能存儲(chǔ)null的key和value
c.Properties
:常用來(lái)處理配置文件。key和value都是String類(lèi)型
HashMap的底層:數(shù)組 + 鏈表(JDK及之前)
數(shù)組 + 鏈表 + 紅黑樹(shù)(jdk 8)
Map結(jié)構(gòu)的理解:
> Map中的key:無(wú)序的、不可重復(fù)的,使用Set存儲(chǔ)所有的key --->key所在的類(lèi)要重寫(xiě)equals()和hashCode()(以HashMap為例)
> Map中的value:無(wú)序的、可重復(fù)的,使用Collection存儲(chǔ)所有的value --->value所在的類(lèi)型要重寫(xiě)equals()
> 一個(gè)鍵值對(duì):key-value構(gòu)成了一個(gè)Entry對(duì)象。
> Map中的entry:無(wú)序的、不可重復(fù)的,使用Set存儲(chǔ)所有的entry
二、Map接口中的常用方法
/* 添加、刪除、修改操作: Object put(Object key,Object value): 將指定key-value添加到(或修改)當(dāng)前map對(duì)象中 void putAll(Map m):將m中的所有key-value對(duì)存放到當(dāng)前map中 Object remove(Object key):移除指定key的key-value對(duì),并返回value void clear():清空當(dāng)前map中的所有數(shù)據(jù) */ public void test1(){ HashMap map = new HashMap(); //添加 map.put("AA",123); map.put(45,123); map.put("BB",56); //修改 map.put("AA",87); System.out.println(map);//{AA=87, BB=56, 45=123} HashMap map1 = new HashMap(); map.put("CC",123); map.put("DD",123); map.putAll(map1); System.out.println(map);//{AA=87, BB=56, CC=123, DD=123, 45=123} //remove(Object key) Object value = map.remove("CC"); System.out.println(value);//123 System.out.println(map);//{AA=87, BB=56, DD=123, 45=123} //clear() map.clear();//與map = null操作不同;map對(duì)象還在,只是里面的數(shù)據(jù)沒(méi)了 System.out.println(map.size());//0 System.out.println(map);//{} }
/* 元素查詢(xún)的操作: Object get(Object key):獲取指定key對(duì)應(yīng)的value boolean containsKey(Object key):是否包含指定的value int size():返回map中key-value對(duì)的個(gè)數(shù) boolean isEmpty():判斷當(dāng)前map是否為空 boolean equals(Object obj):判斷當(dāng)前map和參數(shù)對(duì)象obj是否相等 */ public void test2(){ HashMap map = new HashMap(); map.put("AA",123); map.put(45,123); map.put("BB",56); //Object get(Object key) System.out.println(map.get(45));//123 //containsKey(Object key) boolean isExit = map.containsKey("BB"); System.out.println(isExit);//true isExit = map.containsValue(123); System.out.println(isExit);//true map.clear(); System.out.println(map.isEmpty());//true }
/* 元視圖操作的方法: Set keySet():返回所有key構(gòu)成的Set集合 Collection values(): 返回所有value構(gòu)成的Collection集合 Set entrySet():返回所有key-value對(duì)構(gòu)成的Set集合 */ public void test3(){ HashMap map = new HashMap(); map.put("AA",123); map.put(45,123); map.put("BB",56); //遍歷所有的key集:keySet() Set set = map.keySet(); Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next());//AA BB 45 } System.out.println(); //遍歷所有的value集:value() Collection values = map.values(); for(Object obj : values){ System.out.println(obj);// 123 56 123 } System.out.println(); //遍歷所有的key-value //方式一:entrySet() Set entrySet = map.entrySet(); Iterator iterator1 = entrySet.iterator(); while(iterator1.hasNext()){ Object obj = iterator1.next(); //entrySet集合中的元素都是entry Map.Entry entry = (Map.Entry)obj; System.out.println(entry.getKey() + "---->" + entry.getValue()); //AA---->123 //BB---->56 //45---->123 } System.out.println(); //方式二: Set keySet = map.keySet(); Iterator iterator2 = keySet.iterator(); while(iterator2.hasNext()){ Object key = iterator2.next(); Object value = map.get(key); System.out.println(key + "======" + value); //AA======123 //BB======56 //45======123 } }
總結(jié):常用方法
添加:put(Object key,Object value)
刪除:remove(Object key)
修改:put(Object key,Object value)
查詢(xún):get(Object key)
長(zhǎng)度:size()
遍歷:keySet() / values() / entrySet()
三、源碼分析
1.HashMap的底層實(shí)現(xiàn)原理?
以jdk7為例說(shuō)明:
HashMap map = new HashMap():
在實(shí)例化以后,底層創(chuàng)建了長(zhǎng)度是16的一維數(shù)組Entry[] table.
...可能已經(jīng)執(zhí)行過(guò)多次put...
map.put(key1,value1):
首先,調(diào)用key1所在類(lèi)的hashCode()計(jì)算key1哈希值,此哈希值經(jīng)過(guò)某種算法計(jì)算以后,得到在Entry數(shù)組中的存放位置。
如果此位置上的數(shù)據(jù)為空,此時(shí)的key1-value1添加成功。----情況1
如果此位置上的數(shù)據(jù)不為空,(意味著此位置上存在一個(gè)或多個(gè)數(shù)據(jù)(以鏈表形式存在)),比較key1和已經(jīng)存在的一個(gè)或多個(gè)數(shù)據(jù)
的哈希值:
如果key1的哈希值與已經(jīng)存在的數(shù)據(jù)的哈希值都不相同,此時(shí)key-value1添加成功。----情況2
如果key1的哈希值和已經(jīng)存在的某一個(gè)數(shù)據(jù)(key-value2)的哈希值相同,繼續(xù)比較:調(diào)用key1所在類(lèi)的equals(key2)方法
如果equals()返回false:此時(shí)key1-value1添加成功。----情況3
如果equals()返回true:使用value1替換value2.(修改作用的體現(xiàn))
補(bǔ)充:關(guān)于情況2和情況3:此時(shí)key1-value1和原來(lái)的數(shù)據(jù)以鏈表的方式存儲(chǔ)。
在不斷地添加過(guò)程中,會(huì)涉及到擴(kuò)容問(wèn)題,當(dāng)超出臨界值(且要存放的位置非空)時(shí),擴(kuò)容。默認(rèn)的擴(kuò)容方式:擴(kuò)容為原來(lái)容量的兩倍,并將原有的數(shù)據(jù)復(fù)制過(guò)來(lái)。
jdk8相較于jdk7在底層實(shí)現(xiàn)方面的不同:
1.new HashMap():底層沒(méi)有創(chuàng)建一個(gè)長(zhǎng)度為16的數(shù)組
2.jdk 8底層的數(shù)組是:Node[],而非Entry[]
3.首次調(diào)用put()方法時(shí),底層創(chuàng)建長(zhǎng)度為16的數(shù)組
4.jdk7底層結(jié)構(gòu)只有:數(shù)組 + 鏈表。jdk8中底層結(jié)構(gòu):數(shù)組 + 鏈表 + 紅黑樹(shù)。
5. 形成鏈表時(shí),七上八下(jdk7:新的元素指向舊的元素;jdk8:舊的元素指向新的元素)
當(dāng)數(shù)組的某一個(gè)索引位置上的元素以鏈表形式存在的數(shù)據(jù)個(gè)數(shù) > 8 且當(dāng)前數(shù)組的長(zhǎng)度 > 64時(shí)。
此時(shí)此索引位置上的所有數(shù)據(jù)改為使用紅黑樹(shù)存儲(chǔ)。(重要優(yōu)化:提高查找效率)
DEFAULT_INITIAL_CAPACITY:HashMap的默認(rèn)容量,16
DEFAULT_LOAD_FACTOR:HashMap的默認(rèn)加載因子:0.75
threshold:擴(kuò)容的臨界值,=容量*填充因子:16*0.75 =>12
TREEIFY_THRESHOLD:Bucket中鏈表長(zhǎng)度大于該默認(rèn)值,轉(zhuǎn)化為紅黑樹(shù):8
MIN_TREEIFY_CAPACITY:桶中的Node被樹(shù)化時(shí)最小的hash表容量:64
2.LinkedHashMap的底層實(shí)現(xiàn)原理(了解)
LinkedHashMap的底層使用的結(jié)構(gòu)與HashMap相同,因?yàn)長(zhǎng)inkedHashMap繼承于HashMap,區(qū)別就在于:LinkedHashMap內(nèi)部提供了Entry,替換HashMap中的Node.
源碼中: static class Entry<K,V> extends HashMap.Node<K,V>{ Entry<K,V> before,after;//能夠記錄添加的元素的先后順序 Entry(int hash, K key, V value,Node<K,V> next){ super(hash, key, value, next); } }
TreeMap
//向TreeMap中添加key-value,要求key必須是有同一個(gè)類(lèi)創(chuàng)建的對(duì)象 //因?yàn)橐凑誯ey進(jìn)行排序:自然排序、定制排序 @Test public void test1() { TreeMap map = new TreeMap(); User u1 = new User("Tom", 23); User u2 = new User("Jerry", 32); User u3 = new User("Jack", 20); User u4 = new User("Rose", 18); map.put(u1, 98); map.put(u2, 89); map.put(u3, 76); map.put(u4, 100); Set entrySet = map.entrySet(); Iterator iterator1 = entrySet.iterator(); while (iterator1.hasNext()) { Object obj = iterator1.next(); //entrySet集合中的元素都是entry Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + "---->" + entry.getValue()); //User{name='Jack', age=20}---->76 //User{name='Jerry', age=32}---->89 //User{name='Rose', age=18}---->100 //User{name='Tom', age=23}---->98 } }
Hashtable
> Hashtable 是個(gè)古老的 Map 實(shí)現(xiàn)類(lèi),JDK1.0就提供了。不同于 HashMap , Hashtable 是線程安 全的。
> Hashtable 實(shí)現(xiàn)原理和 HashMap 相同,功能相同。底層都使用哈希表結(jié)構(gòu),查詢(xún)速度快,很多情況 下可以互用。
> 與 HashMap 不同, Hashtable 不允許使用 null 作為 key 和 value
> 與 HashMap -樣, Hashtable 也不能保證其中 Key - Value 對(duì)的順序
> Hashtable 判斷兩個(gè) key 相等、兩個(gè) value 相等的標(biāo)準(zhǔn),與 HashMap 一致。
四、Collections工具類(lèi)
常用方法及其測(cè)試
public void test1(){ ArrayList list = new ArrayList(); list.add(123); list.add(43); list.add(765); list.add(765); list.add(765); list.add(-97); list.add(0); System.out.println(list); // Collections.reverse(list); // Collections.shuffle(list); // Collections.swap(list,1,2); int frequency = Collections.frequency(list, 765); System.out.println(list); System.out.println(frequency); /* Collections類(lèi)中提供了多個(gè)synchronizedXxx()方法,該方法可使將指定集合包裝成線程同步的集合,從而可以解決多線程 并發(fā)訪問(wèn)集合時(shí)的線程安全問(wèn)題 */ List list1 = Collections.synchronizedList(list); }
public void test2(){ List list = new ArrayList(); list.add(123); list.add(43); list.add(765); list.add(-97); list.add(0); //報(bào)異常:IndexOutOfBoundsException // List dest = new ArrayList(); // Collections.copy(dest,list); //正確的: List dest = Arrays.asList(new Object[list.size()]); System.out.println(dest.size()); Collections.copy(dest,list);//5 System.out.println(dest);//[123, 43, 765, -97, 0] }
到此這篇關(guān)于深入淺出講解Java集合之Map接口的文章就介紹到這了,更多相關(guān)Java Map內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入探究MyBatis插件機(jī)制靈活擴(kuò)展及自定義增強(qiáng)框架能力
這篇文章主要介紹了深入探究MyBatis插件機(jī)制靈活擴(kuò)展及自定義增強(qiáng)框架能力2024-01-01EventBus與Spring Event區(qū)別詳解(EventBus 事件機(jī)制,Spring Event事件機(jī)制)
這篇文章主要介紹了EventBus與Spring Event區(qū)別,需要的朋友可以參考下2020-02-02spring中@Autowire和@Resource的區(qū)別在哪里(推薦)
這篇文章主要介紹了spring中@Autowire和@Resource的區(qū)別在哪里?本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02java 中HashMap實(shí)現(xiàn)原理深入理解
這篇文章主要介紹了java 中HashMap實(shí)現(xiàn)原理深入理解的相關(guān)資料,需要的朋友可以參考下2017-03-03Mybatis pagehelper分頁(yè)插件使用過(guò)程解析
這篇文章主要介紹了mybatis pagehelper分頁(yè)插件使用過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02基于SpringBoot框架管理Excel和PDF文件類(lèi)型
這篇文章主要介紹了基于SpringBoot框架,管理Excel和PDF文件類(lèi)型,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-02-02使用Shiro實(shí)現(xiàn)登錄成功后跳轉(zhuǎn)到之前的頁(yè)面
這篇文章主要介紹了如何使用Shiro實(shí)現(xiàn)不同用戶(hù)登錄成功后跳轉(zhuǎn)到不同主頁(yè),實(shí)現(xiàn)此功能目前比較好的方法是用ajax的方法登錄,第二種方法是把用戶(hù)未登錄前的url存在session中,需要的朋友可以參考下2015-07-07MybatisPlus使用Wrapper實(shí)現(xiàn)條件查詢(xún)功能
這篇文章主要介紹了MybatisPlus使用Wrapper實(shí)現(xiàn)查詢(xún)功能,使用它可以實(shí)現(xiàn)很多復(fù)雜的查詢(xún),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-06-06