Java集合框架:LinkedHashMap
歡迎支持筆者新作:《深入理解Kafka:核心設(shè)計(jì)與實(shí)踐原理》和《RabbitMQ實(shí)戰(zhàn)指南》,同時(shí)歡迎關(guān)注筆者的微信公眾號:朱小廝的博客。
歡迎跳轉(zhuǎn)到本文的原文鏈接:https://honeypps.com/java/java-collection-linkedhashmap/
??如無特殊說明,本文以jdk7為準(zhǔn)進(jìn)行說明。
package java.util; import java.io.*; public class LinkedHashMap<K,V>extends HashMap<K,V>implements Map<K,V>{ }??可以看到LinkedHashMap繼承了HashMap,那么LinkedHashMap又有什么特點(diǎn)呢?
??LinkedHashMap是Hash表和鏈表的實(shí)現(xiàn),并且依靠著雙向鏈表保證了迭代順序是插入的順序。非線程安全。
??首先來段代碼(與HashMap那篇類似):
??輸出結(jié)果:
s1:1 s2:2 s3:3 s4:4 s5:5 null:11 s6:6 s7:7 s8:8 {s1=1, s2=2, s3=3, s4=4, s5=5, null=11, s6=6, s7=7, s8=8}??通過結(jié)果可以看到,LinkedHashMap會記錄插入的順序,允許null的鍵值,當(dāng)key值重復(fù)時(shí),后面的會替換前面的。
??通過工作看下LinkedHashMap的結(jié)構(gòu)圖(ppt畫圖不容易,求點(diǎn)贊~)
??可以看到LinkedHashMap比HashMap多了一個(gè)頭指針head(private權(quán)限),header指針是一個(gè)標(biāo)記指針不存儲任何數(shù)據(jù)。標(biāo)記after和before兩個(gè)指針。
??可以看到bullet的實(shí)體Entry也比HashMap的Entry多了before和after兩個(gè)指針。(private static class Entry<K,V> extends HashMap.Entry<K,V>{Entry<K,V> before, after;} )
??LinkedHashMap還有一個(gè)私有變量accessOrder(private final boolean accessOrder;),默認(rèn)為false,即按照插入順序遍歷,譬如開篇的例子中,如果設(shè)置為true則按照訪問順序遍歷,只能通過這個(gè)構(gòu)造函數(shù)設(shè)置:
public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {super(initialCapacity, loadFactor);this.accessOrder = accessOrder;}??接下來舉個(gè)例子(和開篇的例子類似):
Map<String,Integer> map = new LinkedHashMap<>(16,0.75f,true);map.put("s1", 1);map.put("s2", 2);map.put("s3", 3);map.put("s4", 4);map.put("s5", 5);map.put(null, 9);map.put("s6", 6);map.put("s7", 7);map.put("s8", 8);map.put(null, 11);map.get("s6");for(Map.Entry<String,Integer> entry:map.entrySet()){System.out.println(entry.getKey()+":"+entry.getValue());}??輸出結(jié)果:
s1:1 s2:2 s3:3 s4:4 s5:5 s7:7 s8:8 null:11 s6:6??可以看到遍歷順序改為了訪問順序。
如果將上面的遍歷方式改為:
for(Iterator<String> iterator = map.keySet().iterator();iterator.hasNext();){String name = iterator.next();System.out.println(name+"->"+map.get(name));}運(yùn)行結(jié)果出人意料:
s1->1 Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.LinkedHashMap$LinkedHashIterator.nextEntry(Unknown Source)at java.util.LinkedHashMap$KeyIterator.next(Unknown Source)at collections.map.LinkedHashMapTest.main(LinkedHashMapTest.java:33)??LinkedHashMap非但沒有排序,反而程序出現(xiàn)了異常,這是為什么呢?
??ConcurrentModificationException異常一般會在集合迭代過程中被修改事拋出。不僅僅是LinkedHashMap,所有的集合都不允許在迭代器模式中修改集合的結(jié)構(gòu)。一般認(rèn)為,put()、remove()方法會修改集合的結(jié)構(gòu),因此不能在迭代器中使用。但是,這段代碼中并沒有出現(xiàn)類似修改集合結(jié)構(gòu)的代碼,為何也會發(fā)生這樣的問題?
??問題就出在get()方法上。雖然一般認(rèn)為get()方法是只讀的,但是當(dāng)前的LinkedHashMap缺工作在按照元素訪問順序排序的模式中,get()方法會修改LinkedHashMap中的鏈表結(jié)構(gòu),以便將最近訪問的元素放置到鏈表的末尾,因此,這個(gè)操作便引起了這個(gè)錯(cuò)誤。所以,當(dāng)LinkedHashMap工作在這個(gè)模式時(shí),不能再迭代器中使用get()操作。Map的遍歷建議使用entrySet的方式。
>不要在迭代器模式中修改被迭代的集合。如果這么做,就會拋出ConcurrentModificationException異常。這個(gè)特性適用于所有的集合類,包括HashMap,Vector,ArrayList等。
??LinkedHashMap可以根據(jù)訪問順序排序。那這個(gè)功能有什么牛逼之處呢?提示一下:LRU
??LinkedHashMap提供了一個(gè)protected的方法:
??LinkedHashMap中的put方法直接繼承HashMap的put方法,并沒有重寫,但是put方法中需要用到addEntry方法,并且LinkedHashMap對其進(jìn)行了重寫如下:
void addEntry(int hash, K key, V value, int bucketIndex) {super.addEntry(hash, key, value, bucketIndex);// Remove eldest entry if instructedEntry<K,V> eldest = header.after;if (removeEldestEntry(eldest)) {removeEntryForKey(eldest.key);}}??方法中調(diào)用了removeEldestEntry()方法,如果重寫這個(gè)方法就可以實(shí)現(xiàn)LRU。
??譬如:
??引用《關(guān)于Java集合的小抄》來做一下總結(jié):
??擴(kuò)展HashMap增加雙向鏈表的實(shí)現(xiàn),號稱是最占內(nèi)存的數(shù)據(jù)結(jié)構(gòu)。支持iterator()時(shí)按Entry的插入順序來排序(但是更新不算, 如果設(shè)置accessOrder屬性為true,則所有讀寫訪問都算)。
??實(shí)現(xiàn)上是在Entry上再增加屬性before/after指針,插入時(shí)把自己加到Header Entry的前面去。如果所有讀寫訪問都要排序,還要把前后Entry的before/after拼接起來以在鏈表中刪除掉自己。
參考資料:
歡迎跳轉(zhuǎn)到本文的原文鏈接:https://honeypps.com/java/java-collection-linkedhashmap/
歡迎支持筆者新作:《深入理解Kafka:核心設(shè)計(jì)與實(shí)踐原理》和《RabbitMQ實(shí)戰(zhàn)指南》,同時(shí)歡迎關(guān)注筆者的微信公眾號:朱小廝的博客。
總結(jié)
以上是生活随笔為你收集整理的Java集合框架:LinkedHashMap的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java集合框架:HashMap
- 下一篇: Java集合框架:TreeMap