Java(7-3)

Part1
7-2介紹的是集合中的集,現在我們要說一下關於集合的映射:

Java類庫爲映射提供了兩個通用的實現,HashMap和TreeMap。這兩個類都實現了Map接口。 散列映射對建進行散列,樹映射用鍵的整體順序對元素進行排序,並將其組織成搜索樹。散列和比較函數只能用於鍵 ,與鍵關聯的值不能進行散列或比較。

Tips:
每當往映射中添加元素對象時,必須提供一個鍵值。
每當想要檢索一個對象時,必須使用一個鍵。
如果在映射中沒有與給定建對應的消息,get將會返回null。
remove方法用於從映射中刪除給定鍵對應的元素。
size方法用於返回映射中的元素數。
要想迭代處理映射的鍵和值,最容易的方法是forEach方法。可以提供一個接受鍵的lambda表達式。

鍵必須是唯一的! 。不能對同一個鍵存放兩個值。如果對同一個鍵兩次調用put方法,第二個值就會取代第一個值。

這裏有個很實用的技巧,更新映射項
現在我們考慮一種情況,即鍵第一次出現,我們要使用一個映射統計一個單詞在文件中出現的頻度,也就是說,看到一個單詞時,我們將計數器增一,如下所示:

counts.put(word,counts(word)+1);

這是可以的,但是有一種很危險的情況!就是第一次看見word時,在這種情況下,get會犯回一個NullPointerException的異常。

對於這個問題,我們有一個簡單的補救,可以使用getOrDefault方法:

conuts.put(word,counts.getOrDefault(word,0) + 1);

還有一種方法,是手先調用putIfAbsent方法。只有當鍵原先存在時纔會放入一個值。

counts.putIfAbsent(word,0);
counts.put(word,counts.get(word) + 1);

但是這仍不是最好的方法,merge方法可以簡化這個常見的操作。如果原先鍵不存在,下面的調用:

counts.merge(word,1,Integer::sum);

這行代碼的含義是,把word和1關聯,否則使用Integer::sum函數組合原值和1。

Part2
我們上面簡單介紹了一些關於映射的知識,不過Java的集合框架本身並不認爲映射本身是一個集合。值得注意的是,我們可以得到映射的視圖——這是實現了Collection接口或某個子接口的對象。

有三種視圖:建集、值集 和 鍵/值對集 。鍵 和 鍵/值對 可以組成一個集。下面的方法會分別返回這三個視圖:

Set<K> keySet()
Collection<V> values()
Set<Map.Entry<K,V>> entrySet()

需要說明的是,keySet不是HashSet和TreeSet這類東西,而是實現了Set接口的另外某個類的對象。而Set接口擴展了Colletion接口。因此,可以像使用集合一樣使用keySet。
例如,可以枚舉一個映射所有的鍵:

Set<String> keys = map.keySet();
for(String key:keys)
{
    do somethings with key
}

如果想同時產看鍵和值,可以通過枚舉條目來避免查找值!如下:

for(Map.Entry<String, Employee>entry : staff.entrySet())
{
    String k = entry.getKey();
    Employee v = entry.getValue();
    do something with k,v
}

奧!對了,如果在鍵集視圖上調用迭代器的remove方法,實際上會從映射中刪除這個建和他關聯的值!但是卻不能向建集視圖中增加元素!

Part3
現在開始我們將會介紹在集合類庫中的幾個專用的映射類。
1.弱散列映射
設計弱散列映射(WeakHashMap)類是爲了解決一個關於回收的問題。假設一種情況,如果存在一個值,對應鍵已經不再使用了,將會出現什麼情況呢?也就是說,某個鍵的最後一次引用已經消亡,不再有任何途徑引用這個值的對象了。但是,由於程序中任何部分再出現這個鍵,彷彿被忘記了,就無法remove刪除了。 更重要的是java虛擬機的垃圾回收機制,並不能刪除它!。原因是:垃圾回收器跟蹤的是活動的對象。只要映射對象是活動的,其中所有的桶也是活動的,它們就不能被回收! 爲了解決這個問題,就誕生了我們的WeakHashMap類:當對鍵的唯一引用來自散列條目時,這一數據結構將與垃圾回收器協同工作一起刪除鍵/值對。

2.鏈接散裂集與映射
LinkedHashSet和LinkedHashMap類,用來記住插入元素項的順序。這樣就可以避免在散列表中的項從變面上看是隨機排列的。
我們舉一個例子,之前說個散列表的迭代器是隨機枚舉鍵的,但是鏈接散列集就不一樣:

Map<String,Employee> staff = new LinkedHashMap<>();
staff.put("144-25-5464", new Employee("Amy Lee"));
staff.put("567-24-2546", new Employee("Harry Hacker"));
staff.put("157-62-7935", new Employee("Gary Cooper"));
staff.put("456-62-5527", new Employee("Francesca Cruz"));

然後調用迭代器staff.keySet().iterator()
你會發現:144-25-5464,567-24-2546,157-62-7935,456-62-5527。
如果調用的是staff.values().iterator()
你會發現:Amy Lee,Harry Hacker,Gary Cooper,Francesca Cruz

顯然,現在他們的迭代都是存在順序地,不是隨機的了!鏈接散列映射將用訪問順序,而不是插入順序,對映射條目進行迭代。每次抵用get或put,收到影響的條目將從當前位置刪除,並放到條目鏈表的尾部。

3.枚舉集與映射
EmunSet是一個枚舉類型元素集的高效實現。由於枚舉類型只有有限個實例,所以EnumSet內部用位序列實現。如果對性的值在集中,則相應的位被設置爲1。EnumSet,快!

4.標識散列映射
類IdentityHashMap有特殊作用。在這個類中,鍵的散列值不是用hashCode函數計算的,而是用System.identityHahCode方法計算的。這是Object.hashCode方法根據對象的內存地址來計算散列碼時所使用的方法。而且在兩個對象進行比較時,IdentityHashMap類使用==,而不使用equals。
也就說不同的鍵對象,即使內容相同,也被視爲不同的對象。在實現對象遍歷算法時,這個類非常有用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章