????01-集合(Map概述)
? ? ? ? 我們已經(jīng)把集合的半壁江山講完了,就是中間用藍(lán)色框出來那部分:
? ? ? ? 接下來进萄,我們要講Map了捻脖。
? ? ? ? Map和Colletion一樣,它們都屬于集合框架中的頂層接口垮斯,它們兩者外部之間沒有必然的聯(lián)系(當(dāng)然內(nèi)部是有的)郎仆。
? ? ? ? 我們先看一下Map接口的特點(diǎn)。?
? ? ? ? Map接口在java.util這個(gè)包中兜蠕。
? ? ? ? Map集合:該集合存儲(chǔ)鍵值對(duì)扰肌,一對(duì)一對(duì)往里存,而且要保證鍵的唯一性熊杨。
? ? ? ? 那么像這種一對(duì)一對(duì)往里面存的東西應(yīng)用多嗎曙旭?
? ? ? ? 很多呢。
? ? ? ? 我們知道晶府,在存元素的時(shí)候桂躏,ArrayList的特點(diǎn)是可以給每個(gè)元素加上索引,這樣取值比較方便一些川陆,而Map集合就不單單是給元素加索引的問題了剂习,它是直接給元素起名字都行。
? ? ? ? 其實(shí)和生活中一樣较沪,有結(jié)婚的鳞绕,也有單身的,對(duì)于他們我們都要考慮到尸曼。而在Java中们何,Collection中裝的都是單身漢,Map集合裝的是夫妻控轿。
? ? ? ? 用專業(yè)術(shù)語來說冤竹,Collection叫單列集合拂封,而Map叫雙列集合。
? ? ? ? 注意:值是可以重復(fù)的鹦蠕,鍵是不能重復(fù)的冒签。
? ? ? ? Map接口中的方法:
? ? ? ? 1,添加钟病。
? ? ? ? ? ? ? ? put(K key,V value)
? ? ? ? ? ? ? ? putAll(Map<? extends K,? extends V>m)
? ? ? ? 2镣衡,刪除。
? ? ? ? ? ? ? ? clear()
? ? ? ? ? ? ? ? remove(Object key)
? ? ? ? 3档悠,判斷。
? ? ? ? ? ? ? ? containsValue(Object value)
? ? ? ? ? ? ? ? containsKey(Object key)
? ? ? ? ? ? ? ? isEmpty()
? ? ? ? 4望浩,獲取辖所。
? ? ? ? ? ? ? ? get(Object key)
? ? ? ? ? ? ? ? size()
? ? ? ? ? ? ? ? values()
? ? ? ? ? ? ? ? entrySet()
? ? ? ? ? ? ? ? keySet()
? ? ? ? Map集合有三個(gè)子類:Hashtable、HashMap磨德、TreeMap缘回。
????02-集合(Map子類對(duì)象特點(diǎn))
? ? ? ? 在Map集合中有三個(gè)子類:Hashtable、HashMap典挑、TreeMap酥宴。
? ? ? ? 我們先來看一下Hashtable:
? ? ? ? HashMap:
? ? ? ? Map
? ? ? ? ? ? ? ? |——Hashtable:底層是哈希表數(shù)據(jù)結(jié)構(gòu),不可以存入null鍵null值您觉,該集合是線程同步的拙寡。JDK1.0出現(xiàn)的。效率低琳水。
? ? ? ? ? ? ? ? |——HashMap:底層是哈希表數(shù)據(jù)結(jié)構(gòu)肆糕,可以存入null鍵null值,該集合是不同步的在孝。JDK1.2出現(xiàn)的诚啃。效率高。
? ? ? ? ? ? ? ? |——TreeMap:底層是二叉樹數(shù)據(jù)結(jié)構(gòu)私沮,線程不同步始赎,可以用于給map集合中的鍵進(jìn)行排序。
? ? ? ? 我們發(fā)現(xiàn)仔燕,Map和Set很像造垛。其實(shí),Set底層就是使用了Map集合涨享。(Set集合的方法底層調(diào)用的都是Map集合的方法筋搏,Map可以存一對(duì),Set去掉值厕隧,只剩一個(gè))
????03-集合(Map共性方法)
? ? ? ? 接下來說一下Map所涉及到的基本常見的方法奔脐。
? ? ? ? 右邊new的是子類對(duì)象俄周,在這里new誰都可以,主要是演示一下共性方法:
? ? ? ? 小tips:
? ? ? ? 我們也可以通過get方法的返回值(是否為null)來判斷某一個(gè)鍵是否存在髓迎。
? ? ? ? 我們可以這樣存嗎峦朗?
? ? ? ? 我們?cè)囍@取一下:
? ? ? ? 發(fā)現(xiàn)是可以存進(jìn)去的:
? ? ? ? 這就說明了在HashMap集合中,空是可以作為鍵存在的排龄。
? ? ? ? 但這種情況很少見波势,沒有人沒事拿null作為鍵值。
? ? ? ? 接下來用一下這個(gè)方法:
? ? ? ? 用法:
? ? ? ? 打印結(jié)果:
? ? ? ? 但是我們的存放順序是zhangsan1橄维,zhangsan2尺铣,zhangsan3,null争舞,而打印順序和存放順序不一樣凛忿。所以注意HashMap不是按照存放順序來排序的,我們說是無序的(但它是按照哈希值來排序的竞川,并不是亂七八糟順序喔)店溢。
? ? ? ? 另外,我們發(fā)現(xiàn)委乌,add方法返回的是boolean類型床牧,而put方法是返回V的:
? ? ? ? 對(duì)于HashSet集合,添加了"adc"再添加"adc"的時(shí)候遭贸,返回值就是false戈咳,因?yàn)椤癮dc”已經(jīng)存在了。
? ? ? ? 那返回V是什么意思呢壕吹?
? ? ? ? 演示一下:
? ? ? ? 打印結(jié)果:
? ? ? ? 再存一個(gè)同鍵不同值的元素:
? ? ? ? 打印結(jié)果:
? ? ? ? 當(dāng)存了相同鍵的時(shí)候除秀,新的值會(huì)替換老的值,而put方法會(huì)將這個(gè)鍵所對(duì)應(yīng)的原來的值返回來算利。第一次存的時(shí)候册踩,由于還沒有“01”鍵,所以返回為null效拭。
????04-集合(Map-keySet)
? ? ? ? 上節(jié)課暂吉,我們用get方法可以取到某一個(gè)鍵的值,但是這個(gè)方法比較單一缎患,現(xiàn)在我們想把所有鍵的值都取出慕的。
? ? ? ? 但是我們發(fā)現(xiàn)Map并沒有迭代器,所以我們現(xiàn)在的思路是:先拿到所有的鍵挤渔,然后讓每個(gè)鍵都執(zhí)行g(shù)et方法肮街,這樣所有值就都可以拿到了。
? ? ? ? 這個(gè)可以拿到所有鍵的方法判导,叫做keySet:
? ? ? ? 也就是說嫉父,它將Map集合中所有的鍵都存到Set當(dāng)中去了沛硅,而Set具備迭代器,可以用迭代方式取出所有鍵绕辖。
? ? ? ? 演示一下:
? ? ? ? 現(xiàn)在我們拿到了所有鍵的值摇肌,有了鍵就可以通過Map集合的get方法獲取其對(duì)應(yīng)的值:
? ? ? ? 再用畫圖的方式描述一下過程(其實(shí)每個(gè)格子里存的都是引用地址值,這里為了方便理解直接將它們的值畫進(jìn)去了):
????05-集合(Map-entrySet)
? ? ? ? 通過keySet方法仪际,我們發(fā)先Map集合不需要迭代器围小,它的取出原理是將Map集合轉(zhuǎn)成set集合,再通過迭代器取出树碱。
? ? ? ? 接下來我們發(fā)現(xiàn)還有一個(gè)方法:
? ? ? ? 點(diǎn)擊Map.Entry肯适,發(fā)現(xiàn)它是一個(gè)接口,有自己的方法:
? ? ? ? 我們可以通過它的getKey和getValue方法獲取鍵和值成榜。
? ? ? ? 不多說疹娶,先寫代碼演示一下,將Map集合中的映射關(guān)系取出伦连,存入到Set集合中:
? ? ? ? 雖然說這個(gè)方法寫起來有一點(diǎn)點(diǎn)小麻煩,但是取出方式很爽~
? ? ? ? 畫圖表示一下它的取出過程:
? ? ? ? 其實(shí)存到Set中的是一個(gè)個(gè)關(guān)系:Map.Entry钳垮,所以Map.Entry又為我們提供了getKey和getValue方法來獲取這個(gè)關(guān)系中的鍵和值惑淳。
? ? ? ? 打個(gè)比方,之前的keyset方法饺窿,取出來的是丈夫的身份證歧焦,然后通過丈夫的身份證號(hào)來找到他對(duì)應(yīng)的妻子;而entrySet方法肚医,取出來的是他們的結(jié)婚證绢馍,其中又提供了方法獲取結(jié)婚證中丈夫的信息和妻子的信息。
? ? ? ? 這個(gè)方法用完之后肠套,我們來解釋一下什么叫Map.Entry<K,V>舰涌。
? ? ? ? 其實(shí)Entry也是一個(gè)接口,它是Map接口中的一個(gè)內(nèi)部接口:
? ? ? ? 我們可以看到Map接口中有一個(gè)嵌套類(內(nèi)部接口)摘要:
? ? ? ? 為什么把Entry定義成Map的一個(gè)子接口呢你稚?為什么不把它定義到外部來呢瓷耙?
? ? ? ? 我們想一想,是不是現(xiàn)有Map集合再有這個(gè)映射關(guān)系刁赖,所以這個(gè)映射關(guān)系是Map集合中的內(nèi)部事物搁痛,只有有了Map集合才能有關(guān)系。而且宇弛,這個(gè)關(guān)系是在直接訪問Map集合中的元素鸡典。綜上,把它定義成了內(nèi)部規(guī)則枪芒。
? ? ? ? 注意彻况,Map.Entry<K,V>是靜態(tài)的:
? ? ? ? 而能加static的接口都是內(nèi)部接口谁尸,因?yàn)橹挥薪涌谠诔蓡T位置上才能加靜態(tài)修飾符。
? ? ? ? 總結(jié)一下:
? ? ? ? map集合的兩種取出方式:
? ? ? ? 1疗垛,Set<K> keySet:將Map中所有的鍵存入到Set集合症汹,因?yàn)镾et具備迭代器。所以可以用迭代方式取出所有的鍵贷腕,再根據(jù)get方法背镇,獲取每一個(gè)鍵對(duì)應(yīng)的值。
? ? ? ? 2泽裳,Set<Map.Entry<k,v>> entrySet:將Map集合中的映射關(guān)系存入到了Set集合中瞒斩,而這個(gè)關(guān)系的數(shù)據(jù)類型就是:Map.Entry。
? ??06-集合(Map練習(xí))
? ? ? ? 接下來做一個(gè)練習(xí)涮总,需求:
? ? ? ? 我們分成三步完成:1胸囱,描述學(xué)生。2瀑梗,定義Map容器烹笔,將學(xué)生作為鍵,地址作為值抛丽,存入谤职。3,獲取Map集合中的元素亿鲜。
? ? ? ? 接下來用代碼來實(shí)現(xiàn):
? ? ? ? Student描述好了允蜈,但是像這樣的事物產(chǎn)生的會(huì)非常的多,一般是要存的蒿柳,存的時(shí)候有可能就會(huì)存到HashSet中去饶套,如果存到HashSet中去,就要判斷一下元素的重復(fù)的條件垒探。但是這個(gè)類不寫hashCode和equals方法妓蛮,它比較的就是元素的地址值,而我們應(yīng)該比較的是按照自定義的元素自身的條件特點(diǎn)來判斷唯一性圾叼。
? ? ? ? 所以仔引,我們需要復(fù)寫hashCode方法和equals方法:
? ? ? ? 因?yàn)镾tudent可能會(huì)產(chǎn)生很多對(duì)象,所以這些對(duì)象也需要一些自然順序褐奥,因此讓它實(shí)現(xiàn)Comparable接口咖耘,從而具備可比性:
? ? ? ? 重寫compareTo方法:
? ? ? ? 記住,凡事這種能同時(shí)創(chuàng)建多個(gè)對(duì)象需要被用的時(shí)候撬码,一定要做這些動(dòng)作:重寫hashCode方法儿倒,重寫equals方法,實(shí)現(xiàn)Comparable接口、重寫compareTo方法夫否。
? ? ? ? 對(duì)象描述完了彻犁,存就對(duì)了。
? ? ? ? 別忘了導(dǎo)入包哦:
? ? ? ? 存入&取出的代碼(先用keySet來實(shí)現(xiàn)):
? ? ? ? 接下來用第二種取出方式entrySet來實(shí)現(xiàn):
? ? ? ? 我們將hashCode方法和equals方法注釋掉凰慈,存同一鍵值的元素試試:
? ? ? ? 運(yùn)行后發(fā)現(xiàn)同一鍵值的兩個(gè)元素都存進(jìn)來了汞幢,沒有保證鍵的唯一性,所以重寫hashCode方法和equals方法是十分必要的微谓。
????07-集合(TreeMap練習(xí))
? ? ? ? 上節(jié)的練習(xí)只是對(duì)學(xué)生對(duì)象和對(duì)應(yīng)的地址進(jìn)行了存取森篷,這節(jié)課我們進(jìn)行排序。? ??
????????需求:對(duì)學(xué)生對(duì)象的年齡進(jìn)行升序排序豺型。
? ? ? ? 因?yàn)閿?shù)據(jù)是以鍵值對(duì)形式存在的仲智,所以要使用可以排序的Map集合:TreeMap。
? ? ? ? Student類(寫在MapTest.java文件中):
? ? ? ? 主函數(shù)(寫在MapTest2.java中):
? ? ? ? 先編譯MapTest.java(Student類所在的java文件):
? ? ? ? 再編譯MapTest2.java(主函數(shù)所在的java文件):
? ? ? ? 運(yùn)行姻氨,可以看到取出的是按照年齡來排序的:
? ? ? ? 下面想按照學(xué)生姓名來排序钓辆。
? ? ? ? 我們可以指定一個(gè)比較器:
? ? ? ? 接下來我們自定義一個(gè)姓名比較器:
? ? ? ? 主函數(shù):
? ? ? ? 編譯運(yùn)行:
? ? 08-集合(TreeMap練習(xí)-字母出現(xiàn)的次數(shù))
? ? ? ? 練習(xí):? ?"sdfgzxcvasdfxcvdf"獲取該字符中的字母出現(xiàn)的次數(shù)。
? ? ? ? 希望打印結(jié)果:a(1)c(2)......
? ? ? ? 通過結(jié)果發(fā)現(xiàn)肴焊,每一個(gè)字母都有對(duì)應(yīng)的次數(shù)前联。
? ? ? ? 說明字母和次數(shù)之間都有映射關(guān)系。
? ? ? ? 注意了娶眷,當(dāng)發(fā)現(xiàn)有映射關(guān)系時(shí)似嗤,可以選擇Map集合,因?yàn)镸ap集合中存放的就是映射關(guān)系茂浮。
? ? ? ? 什么時(shí)候使用Map集合呢?
? ? ? ? 當(dāng)數(shù)據(jù)之間存在著映射關(guān)系時(shí)壳咕,就要先想Map集合席揽。
? ? ? ? 接下來將思路畫個(gè)圖:
? ? ? ? 把思路整理一下:
? ? ? ? 1,將字符串轉(zhuǎn)換成字符數(shù)組谓厘,因?yàn)橐獙?duì)每一個(gè)字母進(jìn)行操作幌羞。
? ? ? ? 2,定義一個(gè)Map集合竟稳,因?yàn)榇蛴〗Y(jié)果的字母有順序属桦,所以使用TreeMap集合。
? ? ? ? 3他爸,遍歷字符數(shù)組聂宾,將每一個(gè)字母作為鍵去查Map集合。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 如果返回null诊笤,將該字母和1存入到Map集合中系谐。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 如果返回不是null,說明該字母在Map集合中已經(jīng)存在并有對(duì)應(yīng)次數(shù)。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 那么就獲取該次數(shù)并進(jìn)行自增纪他,然后將該字母和自增后的次數(shù)存入到Map集合中鄙煤,覆? ? ? ? ? 蓋掉鍵原先所對(duì)應(yīng)的值。
? ? ? ? 4茶袒,將Map集合中的數(shù)據(jù)變成指定的字符串形式返回梯刚。
? ? ? ? 字母是char型,數(shù)字是int型薪寓,所以我們?cè)诮reeMap對(duì)象時(shí)要存<char,int>嗎亡资?
? ? ? ? 不是的。
? ? ? ? 因?yàn)榉?b>型里面接收的都是引用數(shù)據(jù)類型预愤,所以必須找到其基本的數(shù)據(jù)類型包裝類沟于,不能直接往里面存char和int。
? ? ? ? 應(yīng)該這樣存:?
? ? ? ? 完整實(shí)現(xiàn)代碼:
? ? ? ? 主函數(shù)中調(diào)用:
? ? ? ? 運(yùn)行:
? ? ? ? 運(yùn)行結(jié)果是沒有問題的植康,現(xiàn)在我們需要將結(jié)果打印成a(4)b(2)......這樣的形式旷太。
? ? ? ? 想換成這種形式該怎么做呢?
? ? ? ? 我們遍歷完之后將結(jié)果存起來销睁。
? ? ? ? 怎么存供璧?往哪里存?用什么容器冻记?
? ? ? ? 緩沖區(qū)睡毒。
? ? ? ? 緩沖區(qū)里什么類型都可以放,而且最終變成字符串冗栗。
? ? ? ? 代碼實(shí)現(xiàn)(緊接著之前的代碼寫):
? ? ? ? 主函數(shù)中接收到一個(gè)返回的字符串演顾,并打印字符串:
? ? ? ? 運(yùn)行:
? ? ? ? 接下來發(fā)現(xiàn)一個(gè)小問題,在這里隅居,這兩個(gè)put的代碼比較重復(fù):
? ? ? ? 我們給它優(yōu)化一下:
? ? ? ? 注意:count變量定義在循環(huán)外比較好哦钠至,這樣只需要開辟一次空間,如果定義在循環(huán)內(nèi)部胎源,每次循環(huán)都會(huì)開辟棉钧、釋放一次count空間。
? ? ? ? 還有個(gè)小問題涕蚤,字符串中可能存在"+"宪卿、"-"、"1"這樣非字母的字符万栅,我們運(yùn)行發(fā)現(xiàn)它們都會(huì)被存進(jìn)去:
? ? ? ? 但是我們并不需要它們佑钾,該怎么做呢?
? ? ? ? 每次取出字符的時(shí)候都判斷一下就OK了:
????09-集合(Map擴(kuò)展)
? ? ? ? 接下來說一下Map集合的擴(kuò)展知識(shí)烦粒。
? ? ? ? Map集合被使用時(shí)因?yàn)榫邆溆成潢P(guān)系次绘。
? ? ? ? 現(xiàn)在有一個(gè)例子,一個(gè)學(xué)校有多個(gè)教室,每個(gè)教室中又有多個(gè)學(xué)生邮偎,每個(gè)學(xué)生又有學(xué)號(hào)和姓名:? ? ? ??
? ? ? ? 我們畫圖來表示它們的關(guān)系:
? ? ? ? 這是一個(gè)集合嵌套集合的關(guān)系管跺,比如yureban和它右邊那個(gè)方框存在映射關(guān)系,而這個(gè)方框中禾进,學(xué)號(hào)和姓名又存在著映射關(guān)系豁跑。
? ? ? ? 代碼實(shí)現(xiàn):
? ? ? ? ?現(xiàn)在想取出czbk里的所有學(xué)生。?
? ? ? ? ?拿到了所有教室:
? ? ? ? 接著拿每個(gè)教室里學(xué)生的信息:
? ? ? ? OK的泻云。
? ? ? ? 我們都知道艇拍,學(xué)號(hào)和姓名通常會(huì)封裝成學(xué)生對(duì)象。那我們這個(gè)例子可以改成這樣:
? ? ? ? 這一個(gè)班里對(duì)應(yīng)好多學(xué)生對(duì)象宠纯,而學(xué)生對(duì)象就是一個(gè)對(duì)象卸夕,這時(shí)候就不用Map集合來存了。
? ? ? ? 畫圖表示一下:
? ? ? ? 代碼實(shí)現(xiàn):? ? ? ? ?
? ? ? ? 運(yùn)行:
? ? ? ? 成功婆瓜。