1 集合特性
對于集合框架關(guān)注點(diǎn):
- 集合底層實(shí)現(xiàn)的數(shù)據(jù)結(jié)構(gòu)是什么 數(shù)組+鏈表+紅黑樹
- 集合中元素是否允許為空 是
- 是否允許重復(fù)的數(shù)據(jù) 否
- 是否有序(這里的有序是指讀取數(shù)據(jù)和存放數(shù)據(jù)的順序是否一致) 是
- 是否線程安全。 否
2 LinkedHashMap分析
LinkedHashMap 繼承HashMap
沒有重寫put resize等方法 因此基本數(shù)據(jù)結(jié)構(gòu)是相同的數(shù)組、鏈表、紅黑樹
public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;//維護(hù)插入的順序
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
那么為啥能保持有序呢?先初始化一個(gè)LinkedHashMap
public LinkedHashMap() {
super();//和hashMap一樣
accessOrder = false;//表示順序類型,true查詢,false表示插入
}
/**
* The iteration ordering method for this linked hash map: <tt>true</tt>
* for access-order, <tt>false</tt> for insertion-order.
*
* @serial
*/
final boolean accessOrder;
分析下put方法
put方法沒有重寫,因此和HashMap是一樣的 重寫了newNode
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p);
return p;
}
//這里可以對比下hashMap,少了 linkNodeLast(p)
//這個(gè)地方維護(hù)一個(gè)插入的頭尾關(guān)系,本質(zhì)可以結(jié)合LinkedList的實(shí)現(xiàn)很相似
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
tail = p;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
}
除此之外LinkedHashMap實(shí)現(xiàn)了afterNodeAccess,afterNodeInsertion方法,這個(gè)都依賴accessOrder這個(gè)參數(shù),如果是false則表示原來的結(jié)構(gòu)不會改變,如果為true則會改變以前的結(jié)構(gòu)
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
3 LinkedHashMap和LRU緩存
LRU:最近最少使用算法
來看個(gè)例子:
LinkedHashMap linkedHashMap=new LinkedHashMap();
linkedHashMap.put(1, 1);
linkedHashMap.put(2, 2);
linkedHashMap.put(3, 3);
linkedHashMap.put(4, 4);
linkedHashMap.put(3, 5);
linkedHashMap.get(4);
System.out.println(linkedHashMap.toString());
//輸出結(jié)果為{1=1, 2=2, 3=5, 4=4}沒啥問題
我們改變accessOrder的查詢方式
LinkedHashMap linkedHashMap1= new LinkedHashMap(16, 0.75f, true);
linkedHashMap1.put(1,1);
linkedHashMap1.put(2,2);
linkedHashMap1.put(3, 3);
linkedHashMap1.put(4, 4);
System.out.println(linkedHashMap1.get(2));
System.out.println(linkedHashMap1.get(1));
System.out.println(linkedHashMap1.toString());
//運(yùn)行結(jié)果{3=3, 4=4, 2=2, 1=1}
那么我們可以根據(jù)LinkedHashMap這個(gè)特性寫一個(gè)簡單的LRU緩存
public class LruMap<K,V> extends LinkedHashMap<K,V>{
private int maxSize;
public LruMap(int maxSize) {
super(16,0.75f,true);
this.maxSize = maxSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size()>maxSize;
}
public static void main(String[] args){
LruMap<Integer,Integer> lruMap=new LruMap<>(3);
lruMap.put(1,1);
lruMap.put(2,2);
lruMap.put(3,3);
lruMap.put(4,4);
System.out.println(lruMap.toString());
//運(yùn)行結(jié)果{2=2, 3=3, 4=4} 發(fā)現(xiàn)4已經(jīng)把3替換掉了
}
}
ps:我們知道LinkedHashMap是非線程安全的,所以LruMap這個(gè)緩存在多線程環(huán)境下是有問題的,我們可以重寫起增刪改的方法,加上synchronized關(guān)鍵字或者使用ReentrantLock機(jī)制解決并發(fā)問題~