源碼分析——LinkedHashMap

理解了HashMap就可以很容易理解LinkedHashMap了,首先來看一下LinkedHashMap所處的體系結構:

從上面可以看出LinkedHashMap處於Map接口中,作爲HashMap的一個子類,其繼承了HashMap的方法,並且比它多了一項特性,也就是可以保證輸入的順序和輸出的順序是一樣的。接下來繼續看其如何實現:

public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>

由於它繼承的是HashMap這個類,因此自然也繼承了HashMap的所有接口,所以也就不需要再重複實現了,除此之外,LinkedHashMap還實現了Map接口。

再繼續看其成員變量:

private static final long serialVersionUID = 3801124242820219131L;
//序列化標識符,用於反序列化
		
transient LinkedHashMap.Entry<K,V> head;
//頭結點,不被序列化
		
transient LinkedHashMap.Entry<K,V> tail;
//尾結點,不被序列化
		
final boolean accessOrder;
//設置順序,true:按訪問順序排序,false:按插入順序排序

static class Entry<K,V> extends HashMap.Node<K,V>{
//靜態內部類,繼承自HashMap中的Node內部類
	Entry<K,V> before, after;
	//多了兩個Entry指針,分別指向前一個Entry和後一個Entry
	Entry(int hash, K key, V value, Node<K,V> next){
	//構造器,傳的值也是一樣的
	super(hash,key,value,next);
}

由於LinkedHashMap是繼承了HashMap的,所以也繼承了其中所有的非final修飾的成員變量,因此LinkedHashMap在此繼承上還增加了head、tail、accessOrder三個成員變量,head和tail主要是作爲頭結點和尾結點的標誌,accessOrder是作爲以何種順序排序的判斷,如果爲true,則表示按訪問順序,將最近訪問的放入鏈表的尾部,而爲false,則表示按照插入的順序排序。從此可以看出,LinkedHashMap是在HashMap的基礎上再在所有的鍵值對上面建立一個鏈表,該鏈表的鍵值對排列的順序即accessOrder指定的順序,這樣就達成了有序的特點。提醒一下這個有序並不是代表遞增或者遞減這樣的順序,其實可以說是一種時間順序。

同樣,LinkedHashMap中放置鍵值對的結點Entry也是繼承了HashMap中的Node結點,而且還增加了before和after兩個結點指針。即分別指向順序鏈表中的前置結點和後置結點。

接下來繼續看其構造函數:

public LinkedHashMap(int initialCapacity, float loadFactor){
//構造器1,參數爲初始容量與加載因子
	super(initialCapacity,loadFactor);
	//調用父類的構造器
	accessOrder = false;
	//將accessOrder設置爲false,即按插入順序排序
}
		
public LinkedHashMap(int initialCapacity){
//構造器2,參數爲初始容量
	super(initialCapacity);
	//調用父類構造器
	accessOrder = false;
	//設置accessOrder爲false,按插入順序排序
}
		
public LinkedHashMap(){
//構造器3,空參
	super();
	accessOrder = false;
}
		
public LinkedHashMap(Map<? extends K, ? extends V> m){
//構造器4,參數爲Map類容器
	super();
	accessOrder = false;
	putMapEntries(m,false);
}
		
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder){
//構造器5,參數爲初始容量、加載因子以及訪問順序
//此構造器可以自己設置鏈表排列順序爲插入順序還是訪問順序
	super(initialCapacity, loadFactor);
	this.accessOrder = accessOrder
}

LinkedHashMap包含五個構造器,當傳入的是初始容量與加載因子,則先調用父類構造器,並將accessOrder置爲false,即順序爲插入順序;如果傳入的是初始容量,則將加載因子置爲默認值,將accessOrder也置爲false,再調用父類構造器;當傳入的爲空參,則將直接調用父類空參構造器並將accessOrder置爲false;當傳入的是一個Map集合類,則先調用父類空參構造器,再將accessOrder置爲false,並調用putMapEntries將鍵值對依次放入LinkedHashMap中;當傳入的是一個初始容量、加載因子與accessOrder時,則按照傳入的參數先調用父類構造器,再設置accessOrder。總之,如果不設置accessOrder參數,則其爲默認false,即默認是按照插入順序排序,當然可以通過構造器設置accessOrder爲訪問順序排序。

接下來再研究一下其常用方法:

1.get

public V get(Object key){
//此處重寫的目的是判斷本容器是按插入順序排序還是按訪問順序
//如果是第二種,則當找到了該鍵值對,該鍵值對就得移動到鏈表的尾部
	Node<K,V> e;
	if((e = getNode(hash(key), key)) == null)
		return null;
	if(accessOrder)
		afterNodeAccess(e);
	return e.value;
}

由於LinkedHashMap是直接繼承HashMap類的,所以HashMap中的常用的get、put、remove、clear等方法是可以直接用的,而對於get方法兩者的最大的區別就是當LinkedHashMap的accessOrder設置爲true時,當前訪問的結點要移動的排序鏈表的末尾。因此才重寫get方法。其實此get方法的主體還是HashMap中的get方法。

2.containsValue

public boolean containsValue(Object value){
//判斷鏈表中是否含有value值,由於直接用此類中的鏈表查找
//value時比HashMap中的查找效率要高,所以就重寫了此方法
	for(LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after){
	//遍歷鏈表,如果找到了返回true,沒找到返回false
		V v = e.value;
		if(v == value || (value != null && value.equals(v)))
			return true;
		}
	return false;
}

此方法是通過傳入的value值尋找到第一個value值與其相等的鍵值對,而HashMap中只能通過key來尋找相關的鍵值對,如果通過value來尋找則效率比較低,畢竟其實裏面的每一個鏈表或者紅黑樹都是散列的分佈在數組槽中的,而LinkedHashMap中建立了一個連接所有鍵值對的鏈表,而從這條鏈表中查詢某一個value值就容易的多。因此重寫了此方法。

3.clear

public void clear(){
//清空數組
        super.clear();
	//調用父類清空方法
	head = tail = null;
	//將鏈表也清空
}

此處的清空方法除了要調用父類的清空方法之外還要將兩個頭尾指針置爲空。

總結:LinkedHashMap其實就是在HashMap所建立的哈希表-鏈表-紅黑樹的結構的基礎上再加上一條連接包含所有鍵值對的鏈表,此鏈表中排列順序有兩種方式,方式可以在創建LinkedHashMap時通過設置accessOrder變量選擇,從而實現以插入順序排序和訪問順序排序兩種排序方式。

總代碼:

public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>{
	
	static class Entry<K,V> extends HashMap.Node<K,V>{
		//靜態內部類,繼承自HashMap中的Node內部類
		Entry<K,V> before, after;
		//多了兩個Entry指針,分別指向前一個Entry和後一個Entry
		Entry(int hash, K key, V value, Node<K,V> next){
			//構造器,傳的值也是一樣的
			super(hash,key,value,next);
		}
	}
		
		private static final long serialVersionUID = 3801124242820219131L;
		//序列化標識符,用於反序列化
		
		transient LinkedHashMap.Entry<K,V> head;
		//頭結點,不被序列化
		
		transient LinkedHashMap.Entry<K,V> tail;
		//尾結點,不被序列化
		
		final boolean accessOrder;
		//設置順序,true:按訪問順序排序,false:按插入順序排序
		
		private void linkNodeLast(LinkedHashMap.Entry<K,V> p){
			//在鏈表尾部插入一個結點
			LinkedHashMap.Entry<K,V> last = tail;
			//將尾結點取出賦給last結點
			tail = p;//將尾結點移到p結點處
			if(last == null)
				//如果原尾結點爲空,說明鏈表爲空
				head = p;
				//將p結點作爲頭結點
			else{
				p.before = last;
				//將p的前置指針指向last
				last.after = p;
				//將last的後置指針指向p
			}
		}
		
		private void transferLinks(LinkedHashMap.Entry<K,V> src, LinkedHashMap.Entry<K,V> dst){
			//用dst結點替換掉src結點
			LinkedHashMap.Entry<K,V> b = dst.before = src.before;
			//將src結點的前置結點作爲dst的前置結點且賦給b結點
			LinkedHashMap.Entry<K,V> a = dst.after = src.after;
			//將src結點的後置結點作爲dst的後置結點且賦給a結點
			if(b == null)
				//如果b結點爲空,說明src是頭結點
				head = dst;
				//將dst作爲頭結點
			else
				//否則,將src的前置結點的後置指針由指向src結點變爲指向dst結點
				b.after = dst;
			if(a == null)
				//如果b結點爲空,說明src時尾結點
				tail = dst;
				//將dst作爲尾結點
			else
				//否則,將src的後置結點的前置指針由指向src結點變爲指向dst結點
				a.before = dst;
		}
		
		void reinitialize(){
			//重新初始化
			super.reinitialize();
			//調用父類的初始化方法
			head = tail = null;
			//將頭尾結點置空
		}
		
		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;
			//返回新插入的結點
		}
		
		Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next){
			//將紅黑樹結點轉化爲LinkedHashMap.Entry結點,此處重寫了HashMap中的
			//replacementNode方法
			LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
			//將傳入的p結點強轉爲LinkedHashMap.Entry結點q
			LinkedHashMap.Entry<K,V> t = new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next);
			//重新創建一個結點t,將必要信息寫進去
			transferLinks(q,t);
			//將q與t替換
			return t;
			//返回t結點
		}
		
		TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next){
			//創建一個新的TreeNode,並將其插入到鏈表尾部
			TreeNode<K,V> p = new TreeNode<K,V>(hash,key,value,next);
			linkNodeLast(p);
			return p;
		}
		
		TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next){
			//將LinkedHashMap.Entry結點轉化爲紅黑樹結點,重寫HashMap中的
			//replacementTreeNode方法
			LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
			TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next);
			transferLinks(q,t);
			return t;
		}
		
		void afterNodeRemoval(Node<K,V> e){
			//此方法在HashMap中也聲明瞭,但是沒有寫方法體,而且在刪除結點方法裏面
			//成功刪除後也調用了此方法,因此繼承自HashMap的本類只需要重寫此方法就可以
			//不必再重寫刪除方法,懶惰是人類進步的階梯(^~^)
			//傳進來的是被刪除的結點e,此方法是將e結點從插入順序這條雙向鏈表中刪去
			LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
			//p表示傳進來的e結點,b表示e結點的前置結點,a表示e結點的後置結點
			p.before = p.after = null;
			//先將結點的前置指針和後置指針置爲空
			if(b == null)
				//如果e結點的前置結點是空,說明e結點是首節點
				head = a;
				//將e結點的後置結點作爲首結點
			else
				b.after = a;
				//否則,將e結點的前置結點的後置指針指向e結點的後置結點就夠了
			if(a == null)
				//同理,如果e結點的後置結點爲空,說明e結點是尾結點
				tail = b;
				//將e結點的前置結點作爲尾結點
			else
				a.before = b;
				//否則,將e結點的後置結點的前置指針指向e結點的前置結點就夠了
		}
		
		void afterNodeInsertion(boolean evict){
			//在插入新結點的時候刪除最老的結點,由於removeEldestEntry返回的false
			//所以此方法無法執行,特定場合需要時可以重寫removeEldestEntry方法
			LinkedHashMap.Entry<K,V> first;
			if(evict && (first = head) != null && removeEldestEntry(first)){
				K key = first.key;
				removeNode(hash(key), key, null, false, true);
			}
		}
		
		void afterNodeAccess(Node<K,V> e){
			//當accessOrder爲true時,表示鏈表是按照訪問順序排序的,
			//也就是按被訪問的時間進行排序,已經訪問的往後放。而e結點
			//表示剛被訪問過,因此需要將其移動到鏈表的尾部(此方法在getNode())
			//方法中被調用
			LinkedHashMap.Entry<K,V> last;//先設置一個last結點
			if(accessOrder && (last = tail) != e){
				//如果accessOrder爲true且e不是尾結點
				LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
				//p表示e結點,b表示e的前置結點,a表示e的後置結點
				p.after = null;
				//先將p的後置指針置空
				if(b == null)
					//如果e的前置指針爲空,說明e是首結點
					head = a;
					//將e的後置結點作爲首結點
				else
					b.after = a;
					//否則將e的前置結點的後置指針指向其後置結點
				if(a != null)
					//如果e的後置結點不爲空,說明其不是尾結點
					a.before = b;
					//則將e的後置結點的前置指針指向其前置結點
				else
					last = b;
					//否則說明e是尾結點,則將其前置結點作爲尾結點
				
				//上面主要是將e結點從鏈表中刪除,而下面則將e結點插在鏈表尾部
				
				if(last == null)
					//如果鏈表爲空,則將e作爲首結點
					head = p;
				else{
					//否則將e的前置指針指向last
					p.before = last;
					//將last的後置指針指向e
					last.after = p;
				}
				tail = p;
				//將p結點作爲尾結點
				++modCount;
			}
		}
		
		void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException{
			//按照順序將LinkedHashMap寫入進去
			for(LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after){
				s.writeObject(e.key);
				s.writeObject(e.value);
			}
		}
		
		public LinkedHashMap(int initialCapacity, float loadFactor){
			//構造器1,參數爲初始容量與加載因子
			super(initialCapacity,loadFactor);
			//調用父類的構造器
			accessOrder = false;
			//將accessOrder設置爲false,即按插入順序排序
		}
		
		public LinkedHashMap(int initialCapacity){
			//構造器2,參數爲初始容量
			super(initialCapacity);
			//調用父類構造器
			accessOrder = false;
			//設置accessOrder爲false,按插入順序排序
		}
		
		public LinkedHashMap(){
			//構造器3,空參
			super();
			accessOrder = false;
		}
		
		public LinkedHashMap(Map<? extends K, ? extends V> m){
			//構造器4,參數爲Map類容器
			super();
			accessOrder = false;
			putMapEntries(m,false);
		}
		
		public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder){
			//構造器5,參數爲初始容量、加載因子以及訪問順序
			//此構造器可以自己設置鏈表排列順序爲插入順序還是訪問順序
			super(initialCapacity, loadFactor);
			this.accessOrder = accessOrder
		}
		
		public boolean containsValue(Object value){
			//判斷鏈表中是否含有value值,由於直接用此類中的鏈表查找
			//比HashMap中的查找效率要高,所以就重寫了此方法
			for(LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after){
				//遍歷鏈表,如果找到了返回true,沒找到返回false
				V v = e.value;
				if(v == value || (value != null && value.equals(v)))
					return true;
			}
			return false;
		}
		
		public V get(Object key){
			//此處重寫的目的是判斷本容器是按插入順序排序還是按訪問順序
			//如果是第二種,則當找到了該鍵值對,該鍵值對就得移動到鏈表的尾部
			Node<K,V> e;
			if((e = getNode(hash(key), key)) == null)
				return null;
			if(accessOrder)
				afterNodeAccess(e);
			return e.value;
		}
		
		public V getOrDefault(Object key, V defaultValue){
			//此方法的重寫目的和上一個方法一樣
			Node<K,V> e;
			if((e = getNode(hash(key),key)) == null)
				return defaultValue;
			if(accessOrder)
				afterNodeAccess(e);
			return e.value;
		}
		
		public void clear(){
			//清空數組
			super.clear();
			//調用父類清空方法
			head = tail = null;
			//將鏈表也清空
		}
		
		protected boolean removeEldestEntry(Map.Entry<K,V> eldest){
			//表示插入新結點時是否需要刪除最老的結點
			return false;
		}
		
		public Set<K> keySet(){
			//獲得一個包含key值的Set容器
			Set<K> ks = KeySet;
			if(ks == null){
				ks = new LinkedKeySet();
				keySet = ks;
			}
			return ks;
		}
		
		final class LinkedKeySet extends AbstractSet<K>{
			//和HashMap中的KeySet基本類似,主要是遍歷部分變簡單了
			public final int size(){
				return size;
			}
			public final void clear(){
				LinkedHashMap.this.clear();
			}
			public final Iterator<K> iterator(){
				return new LinkedKeyIterator();
			}
			public final boolean contains(Objects o){
				return containsKey(o);
			}
			public final boolean remove(Object key){
				return removeNode(hash(key), key, null, false, true) != null;
			}
			public final Spliterator<K> spliterator(){
			return Spliterators.spliterator(this, Spliterator.SIZED | Spliterator.ORDERED | Spliterator.DISTINCT);
			}
			public final void forEach(Consumer<? super K> action){
				if(action == null)
					throw new NullPointerException();
				int mc = modCount;
				for(LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
					action.accept(e.key);
				if(modCount != mc)
					throw new ConcurrentModificationException();
			}
		}
		
		public Collection<V> values(){
			//獲得一個包含value值的集合
			Collection<V> vs = values;
			if(vs == null){
				vs = new LinkedValues();
				values = vs;
			}
			return vs;
		}
		
		final class LinkedValues extends AbstractCollection<V>{
			//和HashMap中的values基本類似,主要是遍歷部分變簡單了
			public final int size(){
				return size;
			}
			public final void clear(){
				LinkedHashMap.this.clear();
			}
			public final Iterator<V> iterator(){
				return new LinkedValueIterator();
			}
			public final boolean contains(Object o){
				return containsValue(o);
			}
			public final Spliterator<V> spliterator(){
				return Spliterators.spliterator(this, Spliterator.SIZED | Spliterator.ORDERED);
			}
			public final void forEach(Consumer<? super V> action){
				if(action == null)
					return new NullPointerException();
				int mc = modCount;
				for(LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
					action.accept(e.value);
				if(modCount != mc)
					throw new ConcurrentModificationException();
			}
		}
		
		public Set<Map.Entry<K,V>> entrySet(){
			//獲得一個包含鍵值對的Set集合
			Set<Map.Entry<K,V>> es;
			return (es = entrySet) == null ? (entrySet = new LinkedEntrySet());
		}
		
		final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>>{
			//和HashMap中的EnterySet基本類似,主要是遍歷部分變簡單了
			public final int size(){
				return size;
			}
			public final void clear(){
				LinkedHashMap.this.clear();
			}
			public final Iterator<Map.Entry<K,V>> iterator(){
				return new LinkedEntryIterator();
			}
			public final boolean contains(Object o){
				if(!(o instanceof Map.Entry))
					return false;
				Map.Entry<?,?> e = (Map.Entry<?,?>)o;
				Object key = e.getKey();
				Node<K,V> candidate = getNode(hash(key),key);
				return candidate != null && candidate.equals(e);
			}
			public final boolean remove(Object o){
				if(o instanceof Map.Entry){
					Map.Entry<?,?> e = (Map.Entry<?,?>)o;
					Object key = e.getKey();
					Object value = e.getValue();
					return removeNode(hash(key), key, value, true, true) != null;
				}
				return false;
			}
			public final Spliterator<Map.Entry<K,V>> spliterator(){
				return Spliterators.spliterator(this, Spliterator.SIZED | Spliterator.ORDERED | Spliterator.DISTINCT);
			}
			public final void forEach(Consumer<? super Map.Entry<K,V>> action){
				if(action == null)
					throw new NullPointerException();
				int mc = modCount;
				for(LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
					action.accept(e);
				if(modCount != mc)
					throw new ConcurrentModificationException();
			}
		}
		
		public void forEach(BiConsumer<? super K, ? super V> action){
			//java 1.8新特性
			if(action == null)
				throw new NullPointerException();
			int mc = modCount;
			for(LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
				action.accept(e.key,e.value);
			if(modCount != mc)
				throw new ConcurrentModificationException();
		}
		
		public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function){
			//java 1.8新特性
			if(function == null)
				throw new NullPointerException();
			int mc = modCount;
			for(LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
				e.value = function.apply(e.key, e.value);
			if(modCount != mc)
				throw new ConcurrentModificationException();
		}
		
		abstract class LinkedHashIterator{
			//抽象內部類,迭代器
			LinkedHashMap.Entry<K,V> next;
			LinkedHashMap.Entry<K,V> current;
			int expectedModCount;
			
			LinkedHashIterator(){
				next = head;
				expectedModCount = modCount;
				current = null;
			}
			
			public final boolean hasNext(){
				return next != null;
			}
			
			final LinkedHashMap.Entry<K,V> nextNode(){
				LinkedHashMap.Entry<K,V> e = next;
				if(modCount != expectedModCount)
					throw new ConcurrentModificationException();
				if(e == null)
					throw new NoSuchElementException();
				current = e;
				next = e.after;
				return e;
			}
			
			public final void remove(){
				Node<K,V> p = current;
				if(p == null)
					throw new IllegalStateException();
				if(modCount != expectedModCount)
					throw new ConcurrentModificationException();
				current = null;
				K key = p.key;
				removeNode(hash(key), key, null, false, false);
				expectedModCount = modCount;
			}
		}
		
		final class LinkedKeyIterator extends LinkedHashIterator implements Iterator<K>{
			//key值迭代器
			public final K next(){
				return nextNode().getKey();
			}
		}
		
		final class LinkedValueIterator extends LinkedHashIterator implements Iterator<V<{
			//value值迭代器
			public final V next(){
				return nextNode().value;
			}
		}
		
		final class LinkedEntryIterator extends LinkedHashIterator implements Iterator<Map.Entry<K,V>>{
			//鍵值對迭代器
			public final Map.Entry<K,V> next(){
				return nextNode();
			}
		}
}

參考資料:

https://www.cnblogs.com/graywind/p/9484577.html

https://www.jianshu.com/p/52b1e63b6893

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