????01-集合(Map概述)
? ? ? ? 我們已經(jīng)把集合的半壁江山講完了,就是中間用藍色框出來那部分:

? ? ? ? 接下來,我們要講Map了。
? ? ? ? Map和Colletion一樣,它們都屬于集合框架中的頂層接口,它們兩者外部之間沒有必然的聯(lián)系(當然內(nèi)部是有的)。
? ? ? ? 我們先看一下Map接口的特點。?
? ? ? ? Map接口在java.util這個包中。

? ? ? ? Map集合:該集合存儲鍵值對,一對一對往里存,而且要保證鍵的唯一性。
? ? ? ? 那么像這種一對一對往里面存的東西應用多嗎?
? ? ? ? 很多呢。
? ? ? ? 我們知道,在存元素的時候,ArrayList的特點是可以給每個元素加上索引,這樣取值比較方便一些,而Map集合就不單單是給元素加索引的問題了,它是直接給元素起名字都行。
? ? ? ? 其實和生活中一樣,有結婚的,也有單身的,對于他們我們都要考慮到。而在Java中,Collection中裝的都是單身漢,Map集合裝的是夫妻。
? ? ? ? 用專業(yè)術語來說,Collection叫單列集合,而Map叫雙列集合。
? ? ? ? 注意:值是可以重復的,鍵是不能重復的。
? ? ? ? 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集合有三個子類:Hashtable、HashMap、TreeMap。
????02-集合(Map子類對象特點)
? ? ? ? 在Map集合中有三個子類:Hashtable、HashMap、TreeMap。
? ? ? ? 我們先來看一下Hashtable:


? ? ? ? HashMap:


? ? ? ? Map
? ? ? ? ? ? ? ? |——Hashtable:底層是哈希表數(shù)據(jù)結構,不可以存入null鍵null值,該集合是線程同步的。JDK1.0出現(xiàn)的。效率低。
? ? ? ? ? ? ? ? |——HashMap:底層是哈希表數(shù)據(jù)結構,可以存入null鍵null值,該集合是不同步的。JDK1.2出現(xiàn)的。效率高。
? ? ? ? ? ? ? ? |——TreeMap:底層是二叉樹數(shù)據(jù)結構,線程不同步,可以用于給map集合中的鍵進行排序。
? ? ? ? 我們發(fā)現(xiàn),Map和Set很像。其實,Set底層就是使用了Map集合。(Set集合的方法底層調用的都是Map集合的方法,Map可以存一對,Set去掉值,只剩一個)
????03-集合(Map共性方法)
? ? ? ? 接下來說一下Map所涉及到的基本常見的方法。
? ? ? ? 右邊new的是子類對象,在這里new誰都可以,主要是演示一下共性方法:


? ? ? ? 小tips:
? ? ? ? 我們也可以通過get方法的返回值(是否為null)來判斷某一個鍵是否存在。
? ? ? ? 我們可以這樣存嗎?

? ? ? ? 我們試著獲取一下:

? ? ? ? 發(fā)現(xiàn)是可以存進去的:

? ? ? ? 這就說明了在HashMap集合中,空是可以作為鍵存在的。
? ? ? ? 但這種情況很少見,沒有人沒事拿null作為鍵值。
? ? ? ? 接下來用一下這個方法:

? ? ? ? 用法:

? ? ? ? 打印結果:

? ? ? ? 但是我們的存放順序是zhangsan1,zhangsan2,zhangsan3,null,而打印順序和存放順序不一樣。所以注意HashMap不是按照存放順序來排序的,我們說是無序的(但它是按照哈希值來排序的,并不是亂七八糟順序喔)。
? ? ? ? 另外,我們發(fā)現(xiàn),add方法返回的是boolean類型,而put方法是返回V的:

? ? ? ? 對于HashSet集合,添加了"adc"再添加"adc"的時候,返回值就是false,因為“adc”已經(jīng)存在了。
? ? ? ? 那返回V是什么意思呢?
? ? ? ? 演示一下:

? ? ? ? 打印結果:

? ? ? ? 再存一個同鍵不同值的元素:

? ? ? ? 打印結果:

? ? ? ? 當存了相同鍵的時候,新的值會替換老的值,而put方法會將這個鍵所對應的原來的值返回來。第一次存的時候,由于還沒有“01”鍵,所以返回為null。
????04-集合(Map-keySet)
? ? ? ? 上節(jié)課,我們用get方法可以取到某一個鍵的值,但是這個方法比較單一,現(xiàn)在我們想把所有鍵的值都取出。
? ? ? ? 但是我們發(fā)現(xiàn)Map并沒有迭代器,所以我們現(xiàn)在的思路是:先拿到所有的鍵,然后讓每個鍵都執(zhí)行get方法,這樣所有值就都可以拿到了。
? ? ? ? 這個可以拿到所有鍵的方法,叫做keySet:

? ? ? ? 也就是說,它將Map集合中所有的鍵都存到Set當中去了,而Set具備迭代器,可以用迭代方式取出所有鍵。
? ? ? ? 演示一下:


? ? ? ? 現(xiàn)在我們拿到了所有鍵的值,有了鍵就可以通過Map集合的get方法獲取其對應的值:


? ? ? ? 再用畫圖的方式描述一下過程(其實每個格子里存的都是引用地址值,這里為了方便理解直接將它們的值畫進去了):

????05-集合(Map-entrySet)
? ? ? ? 通過keySet方法,我們發(fā)先Map集合不需要迭代器,它的取出原理是將Map集合轉成set集合,再通過迭代器取出。
? ? ? ? 接下來我們發(fā)現(xiàn)還有一個方法:

? ? ? ? 點擊Map.Entry,發(fā)現(xiàn)它是一個接口,有自己的方法:

? ? ? ? 我們可以通過它的getKey和getValue方法獲取鍵和值。
? ? ? ? 不多說,先寫代碼演示一下,將Map集合中的映射關系取出,存入到Set集合中:


? ? ? ? 雖然說這個方法寫起來有一點點小麻煩,但是取出方式很爽~
? ? ? ? 畫圖表示一下它的取出過程:

? ? ? ? 其實存到Set中的是一個個關系:Map.Entry,所以Map.Entry又為我們提供了getKey和getValue方法來獲取這個關系中的鍵和值。
? ? ? ? 打個比方,之前的keyset方法,取出來的是丈夫的身份證,然后通過丈夫的身份證號來找到他對應的妻子;而entrySet方法,取出來的是他們的結婚證,其中又提供了方法獲取結婚證中丈夫的信息和妻子的信息。
? ? ? ? 這個方法用完之后,我們來解釋一下什么叫Map.Entry<K,V>。
? ? ? ? 其實Entry也是一個接口,它是Map接口中的一個內(nèi)部接口:

? ? ? ? 我們可以看到Map接口中有一個嵌套類(內(nèi)部接口)摘要:

? ? ? ? 為什么把Entry定義成Map的一個子接口呢?為什么不把它定義到外部來呢?
? ? ? ? 我們想一想,是不是現(xiàn)有Map集合再有這個映射關系,所以這個映射關系是Map集合中的內(nèi)部事物,只有有了Map集合才能有關系。而且,這個關系是在直接訪問Map集合中的元素。綜上,把它定義成了內(nèi)部規(guī)則。
? ? ? ? 注意,Map.Entry<K,V>是靜態(tài)的:

? ? ? ? 而能加static的接口都是內(nèi)部接口,因為只有接口在成員位置上才能加靜態(tài)修飾符。
? ? ? ? 總結一下:
? ? ? ? map集合的兩種取出方式:
? ? ? ? 1,Set<K> keySet:將Map中所有的鍵存入到Set集合,因為Set具備迭代器。所以可以用迭代方式取出所有的鍵,再根據(jù)get方法,獲取每一個鍵對應的值。
? ? ? ? 2,Set<Map.Entry<k,v>> entrySet:將Map集合中的映射關系存入到了Set集合中,而這個關系的數(shù)據(jù)類型就是:Map.Entry。
? ??06-集合(Map練習)
? ? ? ? 接下來做一個練習,需求:

? ? ? ? 我們分成三步完成:1,描述學生。2,定義Map容器,將學生作為鍵,地址作為值,存入。3,獲取Map集合中的元素。
? ? ? ? 接下來用代碼來實現(xiàn):

? ? ? ? Student描述好了,但是像這樣的事物產(chǎn)生的會非常的多,一般是要存的,存的時候有可能就會存到HashSet中去,如果存到HashSet中去,就要判斷一下元素的重復的條件。但是這個類不寫hashCode和equals方法,它比較的就是元素的地址值,而我們應該比較的是按照自定義的元素自身的條件特點來判斷唯一性。
? ? ? ? 所以,我們需要復寫hashCode方法和equals方法:

? ? ? ? 因為Student可能會產(chǎn)生很多對象,所以這些對象也需要一些自然順序,因此讓它實現(xiàn)Comparable接口,從而具備可比性:

? ? ? ? 重寫compareTo方法:

? ? ? ? 記住,凡事這種能同時創(chuàng)建多個對象需要被用的時候,一定要做這些動作:重寫hashCode方法,重寫equals方法,實現(xiàn)Comparable接口、重寫compareTo方法。
? ? ? ? 對象描述完了,存就對了。
? ? ? ? 別忘了導入包哦:

? ? ? ? 存入&取出的代碼(先用keySet來實現(xiàn)):


? ? ? ? 接下來用第二種取出方式entrySet來實現(xiàn):


? ? ? ? 我們將hashCode方法和equals方法注釋掉,存同一鍵值的元素試試:


? ? ? ? 運行后發(fā)現(xiàn)同一鍵值的兩個元素都存進來了,沒有保證鍵的唯一性,所以重寫hashCode方法和equals方法是十分必要的。
????07-集合(TreeMap練習)
? ? ? ? 上節(jié)的練習只是對學生對象和對應的地址進行了存取,這節(jié)課我們進行排序。? ??
????????需求:對學生對象的年齡進行升序排序。
? ? ? ? 因為數(shù)據(jù)是以鍵值對形式存在的,所以要使用可以排序的Map集合:TreeMap。
? ? ? ? Student類(寫在MapTest.java文件中):

? ? ? ? 主函數(shù)(寫在MapTest2.java中):

? ? ? ? 先編譯MapTest.java(Student類所在的java文件):

? ? ? ? 再編譯MapTest2.java(主函數(shù)所在的java文件):

? ? ? ? 運行,可以看到取出的是按照年齡來排序的:

? ? ? ? 下面想按照學生姓名來排序。
? ? ? ? 我們可以指定一個比較器:

? ? ? ? 接下來我們自定義一個姓名比較器:

? ? ? ? 主函數(shù):

? ? ? ? 編譯運行:

? ? 08-集合(TreeMap練習-字母出現(xiàn)的次數(shù))
? ? ? ? 練習:? ?"sdfgzxcvasdfxcvdf"獲取該字符中的字母出現(xiàn)的次數(shù)。
? ? ? ? 希望打印結果:a(1)c(2)......
? ? ? ? 通過結果發(fā)現(xiàn),每一個字母都有對應的次數(shù)。
? ? ? ? 說明字母和次數(shù)之間都有映射關系。
? ? ? ? 注意了,當發(fā)現(xiàn)有映射關系時,可以選擇Map集合,因為Map集合中存放的就是映射關系。
? ? ? ? 什么時候使用Map集合呢?
? ? ? ? 當數(shù)據(jù)之間存在著映射關系時,就要先想Map集合。
? ? ? ? 接下來將思路畫個圖:


? ? ? ? 把思路整理一下:
? ? ? ? 1,將字符串轉換成字符數(shù)組,因為要對每一個字母進行操作。
? ? ? ? 2,定義一個Map集合,因為打印結果的字母有順序,所以使用TreeMap集合。
? ? ? ? 3,遍歷字符數(shù)組,將每一個字母作為鍵去查Map集合。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 如果返回null,將該字母和1存入到Map集合中。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 如果返回不是null,說明該字母在Map集合中已經(jīng)存在并有對應次數(shù)。? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 那么就獲取該次數(shù)并進行自增,然后將該字母和自增后的次數(shù)存入到Map集合中,覆? ? ? ? ? 蓋掉鍵原先所對應的值。
? ? ? ? 4,將Map集合中的數(shù)據(jù)變成指定的字符串形式返回。
? ? ? ? 字母是char型,數(shù)字是int型,所以我們在建立TreeMap對象時要存<char,int>嗎?
? ? ? ? 不是的。
? ? ? ? 因為泛型里面接收的都是引用數(shù)據(jù)類型,所以必須找到其基本的數(shù)據(jù)類型包裝類,不能直接往里面存char和int。
? ? ? ? 應該這樣存:?

? ? ? ? 完整實現(xiàn)代碼:

? ? ? ? 主函數(shù)中調用:

? ? ? ? 運行:

? ? ? ? 運行結果是沒有問題的,現(xiàn)在我們需要將結果打印成a(4)b(2)......這樣的形式。
? ? ? ? 想換成這種形式該怎么做呢?
? ? ? ? 我們遍歷完之后將結果存起來。
? ? ? ? 怎么存?往哪里存?用什么容器?
? ? ? ? 緩沖區(qū)。
? ? ? ? 緩沖區(qū)里什么類型都可以放,而且最終變成字符串。
? ? ? ? 代碼實現(xiàn)(緊接著之前的代碼寫):

? ? ? ? 主函數(shù)中接收到一個返回的字符串,并打印字符串:

? ? ? ? 運行:

? ? ? ? 接下來發(fā)現(xiàn)一個小問題,在這里,這兩個put的代碼比較重復:

? ? ? ? 我們給它優(yōu)化一下:

? ? ? ? 注意:count變量定義在循環(huán)外比較好哦,這樣只需要開辟一次空間,如果定義在循環(huán)內(nèi)部,每次循環(huán)都會開辟、釋放一次count空間。
? ? ? ? 還有個小問題,字符串中可能存在"+"、"-"、"1"這樣非字母的字符,我們運行發(fā)現(xiàn)它們都會被存進去:


? ? ? ? 但是我們并不需要它們,該怎么做呢?
? ? ? ? 每次取出字符的時候都判斷一下就OK了:

????09-集合(Map擴展)
? ? ? ? 接下來說一下Map集合的擴展知識。
? ? ? ? Map集合被使用時因為具備映射關系。
? ? ? ? 現(xiàn)在有一個例子,一個學校有多個教室,每個教室中又有多個學生,每個學生又有學號和姓名:? ? ? ??

? ? ? ? 我們畫圖來表示它們的關系:

? ? ? ? 這是一個集合嵌套集合的關系,比如yureban和它右邊那個方框存在映射關系,而這個方框中,學號和姓名又存在著映射關系。
? ? ? ? 代碼實現(xiàn):


? ? ? ? ?現(xiàn)在想取出czbk里的所有學生。?

? ? ? ? ?拿到了所有教室:

? ? ? ? 接著拿每個教室里學生的信息:


? ? ? ? OK的。
? ? ? ? 我們都知道,學號和姓名通常會封裝成學生對象。那我們這個例子可以改成這樣:

? ? ? ? 這一個班里對應好多學生對象,而學生對象就是一個對象,這時候就不用Map集合來存了。
? ? ? ? 畫圖表示一下:

? ? ? ? 代碼實現(xiàn):? ? ? ? ?


? ? ? ? 運行:

? ? ? ? 成功。