`

LinkedHashMap 源码分析

阅读更多
Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)或者是从近期访问最少到近期访问最多的顺序(访问顺序)。

LinkedHashMap性能很可能比 HashMap 稍逊一筹,不过这一点例外:LinkedHashMap 的 collection 视图迭代所需时间与映射的大小 成比例。HashMap 迭代时间很可能开支较大,因为它所需要的时间与其容量 成比例。

LinkedHashMap不是线程安全的。在按插入顺序链接的哈希映射中,仅更改与映射中已包含键关联的值不是结构修改。在按访问顺序链接的哈希映射中,仅利用 get 查询映射是结构修改。

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
{
	// 头节点,不包含实际数据,做定位用。
	private transient Entry<K,V> header;

	// 迭代排序方式:true - 访问顺序, false - 插入顺序
	private final boolean accessOrder;

	// 构造一个带指定初始容量和加载因子的空插入顺序 LinkedHashMap 实例。
	public LinkedHashMap(int initialCapacity, float loadFactor) {
		super(initialCapacity, loadFactor);
		accessOrder = false;
	}

	// 构造一个带指定初始容量和默认加载因子 (0.75) 的空插入顺序 LinkedHashMap 实例。
	public LinkedHashMap(int initialCapacity) {
		super(initialCapacity);
		accessOrder = false;
	}

	// 构造一个带默认初始容量 (16) 和加载因子 (0.75) 的空插入顺序 LinkedHashMap 实例。
	public LinkedHashMap() {
		super();
		accessOrder = false;
	}

	// 构造一个映射关系与指定映射相同的插入顺序 LinkedHashMap 实例。所创建的 LinkedHashMap 实例具有默认的加载因子 (0.75) 和足以容纳指定映射中映射关系的初始容量。
	public LinkedHashMap(Map<? extends K, ? extends V> m) {
		super(m);
		accessOrder = false;
	}

	// 构造一个带指定初始容量、加载因子和排序模式的空 LinkedHashMap 实例。
	public LinkedHashMap(int initialCapacity,
			 float loadFactor,
                         boolean accessOrder) {
		super(initialCapacity, loadFactor);
		this.accessOrder = accessOrder;
	}

	// 实例化时由父类调用该方法,初始化header,前后节点指向自己。
	void init() {
		header = new Entry<K,V>(-1, null, null, null);
		header.before = header.after = header;
	}

	// 把所有的键值对迁移到新的数组中。该方法由父类的resize方法调用。重写该方法以提升性能,使用双向链表迭代会快些。
	void transfer(HashMap.Entry[] newTable) {
		int newCapacity = newTable.length;
		for (Entry<K,V> e = header.after; e != header; e = e.after) {
			int index = indexFor(e.hash, newCapacity);
			e.next = newTable[index];
			newTable[index] = e;
		}
	}

	// 如果此映射将一个或多个键映射到指定值,则返回 true。
	public boolean containsValue(Object value) {
		// Overridden to take advantage of faster iterator
		if (value==null) {
			for (Entry e = header.after; e != header; e = e.after)
				if (e.value==null)
					return true;
		} else {
			for (Entry e = header.after; e != header; e = e.after)
				if (value.equals(e.value))
					return true;
		}
		return false;
	}

	// 返回此映射到指定键的值。如果此映射中没有该键的映射关系,则返回 null 。
	// 更确切地讲,如果此映射包含满足 (key==null ? k==null : key.equals(k)) 的从键 k 到值 v 的映射关系,则此方法返回 v;否则,返回 null。(最多只能有一个这样的映射关系。) 

	// 返回 null 值并不 一定 表明此映射不包含该键的映射关系;也可能此映射将该键显式地映射为 null。可使用 containsKey 操作来区分这两种情况。
	public V get(Object key) {
		Entry<K,V> e = (Entry<K,V>)getEntry(key);
		if (e == null)
			return null;
		e.recordAccess(this);
		return e.value;
	}

	// 从该映射中移除所有映射关系。此调用返回后映射将为空。 
	public void clear() {
		super.clear();
		header.before = header.after = header;
	}

	// 键值对类
	private static class Entry<K,V> extends HashMap.Entry<K,V> {
		// 包含前置和后置节点的引用,构成了双向链表,用于迭代.
		Entry<K,V> before, after;

		Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
			super(hash, key, value, next);
		}

		// 从该双向链表中移除该节点
		private void remove() {
			before.after = after;
			after.before = before;
		}

		// 在存在的某个节点之前加入该节点
		private void addBefore(Entry<K,V> existingEntry) {
			after  = existingEntry;
			before = existingEntry.before;
			before.after = this;
			after.before = this;
		}

		// 不管哪个节点被读取或修改,父类都会调用该方法。如果该LinkedHashMap是访问顺序的,则将节点加到末尾,否则不做任何操作。
		void recordAccess(HashMap<K,V> m) {
			LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
			if (lm.accessOrder) {
				lm.modCount++;
				remove();
				addBefore(lm.header);
			}
		}

		// 记录删除事件
		void recordRemoval(HashMap<K,V> m) {
			remove();
		}
	}

	// 迭代器抽象类
	private abstract class LinkedHashIterator<T> implements Iterator<T> {
		Entry<K,V> nextEntry    = header.after;
		Entry<K,V> lastReturned = null;

		// 期望修改次数初始化为修改次数
		int expectedModCount = modCount;

		public boolean hasNext() {
			return nextEntry != header;
		}

		public void remove() {
			if (lastReturned == null)
				throw new IllegalStateException();
			if (modCount != expectedModCount)
				throw new ConcurrentModificationException();

			LinkedHashMap.this.remove(lastReturned.key);
			lastReturned = null;
			expectedModCount = modCount;
		}

		Entry<K,V> nextEntry() {
			if (modCount != expectedModCount)
				throw new ConcurrentModificationException();
			if (nextEntry == header)
				throw new NoSuchElementException();

			Entry<K,V> e = lastReturned = nextEntry; // 读取当前值
			nextEntry = e.after; // 读取下一个值,保存到nextEntry中
			return e;
		}
	}

	// 键迭代器
	private class KeyIterator extends LinkedHashIterator<K> {
		public K next() { return nextEntry().getKey(); }
	}

	// 值迭代器
	private class ValueIterator extends LinkedHashIterator<V> {
		public V next() { return nextEntry().value; }
	}

	// 键值对迭代器
	private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {
		public Map.Entry<K,V> next() { return nextEntry(); }
	}

	Iterator<K> newKeyIterator()   { return new KeyIterator();   }
	Iterator<V> newValueIterator() { return new ValueIterator(); }
	Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); }

	// 添加键值对到链表的尾部并且根据情况移除最先节点
	void addEntry(int hash, K key, V value, int bucketIndex) {
		createEntry(hash, key, value, bucketIndex);

		// 根据提示移除最先节点,否则适时增大键值对数组
		Entry<K,V> eldest = header.after;
		if (removeEldestEntry(eldest)) {
			removeEntryForKey(eldest.key);
		} else {
			if (size >= threshold)
				resize(2 * table.length);
		}
	}

	// 添加键值对。和addEntry类似,区别是少了移除最先节点和增大键值对数组
	void createEntry(int hash, K key, V value, int bucketIndex) {
		HashMap.Entry<K,V> old = table[bucketIndex];
		Entry<K,V> e = new Entry<K,V>(hash, key, value, old);
		table[bucketIndex] = e;
		e.addBefore(header);
		size++;
	}

	// 是否移除最先节点。在缓存实现中有用。子类可以重写该方法。
	protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
		return false;
	}
}
分享到:
评论

相关推荐

    Java集合系列之LinkedHashMap源码分析

    Java集合系列之LinkedHashMap源码分析 Java集合系列之LinkedHashMap源码分析是Java集合框架中的一部分,主要对LinkedHashMap的源码进行了详细的分析。LinkedHashMap是继承自HashMap的,它重新写了一个Entry,在原来...

    Java集合框架源码分析之LinkedHashMap详解

    Java集合框架源码分析之LinkedHashMap详解 Java集合框架中的LinkedHashMap是HashMap的子类,它继承了HashMap的存储结构,但引入了一个双向链表的头结点,将所有put到LinkedHashMap的节点连接成一个双向循环链表,...

    【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识

    项目相关 项目介绍 使用建议 贡献指南 常见问题 Java 基础 知识点/面试题总结 : (必看 ): Java 基础常见知识点&面试题总结(上) ...LinkedHashMap 核心源码分析 CopyOnWriteArrayList 核心源码分析

    常见的java集合源码分析,以及面试题

    本文将深入剖析Java集合的源码,探讨其内部实现机制,并结合常见面试题,帮助你更好地理解和应用这些知识。 首先,我们从基础开始,Java集合框架主要分为两大类:List(列表)和Set(集合)。List接口包括ArrayList...

    HashMap源码分析

    这篇源码分析将深入探讨HashMap的工作原理和内部实现。 在HashMap中,每个键值对被封装在一个Entry对象中,这些Entry对象存储在一个数组中。当插入新的键值对时,HashMap会根据键的哈希值计算出数组的索引位置。...

    java8集合源码分析-LearningNotes:Java笔记

    集合源码分析 编程笔记 学习、总结、记录 ! —— since 2018/20 :bar_chart: :hot_beverage: :mobile_phone: :laptop: :floppy_disk:  :pager: :globe_with_meridians: :file_cabinet: :books: :bar_chart: 算法和...

    计算机后端-Java-Java核心基础-第25章 集合02 13. LinkedHashMap的底层实现.avi

    计算机后端-Java-Java核心基础-第25章 集合02 12. HashMap在JDK8中的源码分析.avi

    jdk1.8-source:JDK1.8源码分析包

    JDK1.8源码分析 相关的原始码分析结果会以注解的形式体现到原始码中 已完成部分: ReentrantLock CountDownLatch Semaphore HashMap TreeMap LinkedHashMap ConcurrentHashMap 执行器 ...

    Collections源码java-JCF-CodeAnalysis:Javacollectionsframework源码分析

    总的来说,Java集合框架源码分析可以帮助我们掌握集合操作的底层原理,提高代码性能,增强对并发编程的理解,并且有助于我们设计出更高效、更安全的Java程序。通过对Collections类的深入学习,我们可以更好地利用...

    Java集合专题总结:HashMap 和 HashTable 源码学习和面试总结

    Java集合专题总结:HashMap和HashTable源码...本文总结了HashMap和HashTable的源码学习和面试总结,涵盖了它们的存储结构、构造方法、get和put方法的源码分析、Hash表的特点和缺点、HashTable和HashMap的区别等内容。

    HashMap put方法的源码分析

    通过以上分析,我们可以看到Java 1.8 HashMap的put方法在处理哈希冲突时,不仅使用了链表,还在节点数量达到一定阈值时切换为红黑树,有效提高了数据结构的性能。同时,通过扩容机制,HashMap能够保持较低的哈希冲突...

    java二叉树算法源码-JavaCore:Java核心知识。集合框架、JVM机制、多线程与并发框架、网络协议、SSM框架、MySQL、分布式、

    源码分析:LinkedHashMap 集合框架 (第 14 篇) 源码分析:TreeMap 集合框架 (第 15 篇) 源码分析:Set 集合 集合框架 (第 16 篇) 源码分析:BlockingQueue 接口 集合框架 (第 17 篇) 源码分析:...

    Map-main-源码.rar

    本篇文章将主要围绕这些Map的主要实现类进行源码分析,探讨其内部工作原理。 一、HashMap HashMap是最常用的Map实现类,它的核心原理是基于哈希表。HashMap使用一个Entry数组存储键值对,其中Entry是一个内部类,...

    java_collection_source_code_analyze:Java集合部分源码分析-Source code collection

    2. **源码分析** - **容器实现**:这些类的实现通常包括一个内部存储结构(如数组或链表)和一些基本操作,如添加、删除、查找等。例如,ArrayList的`add()`方法会检查容量并根据需要进行扩容,LinkedList的`remove...

    javabitset源码-JerrySoundCode:杰瑞声码

    bitset源码Java源码分析 基础集合列表 ArrayList (done) Vector (done) LinkedList (done) Stack (done) ReferenceQueue (done) ArrayDeque (done) Set HashSet (done) TreeSet (done) LinkedHashSet (done) BitSet ...

    自定义实现常用数据结构 -java版代码.rar

    2. **桶(Bucket)结构**:分析HashMap如何通过哈希值将元素分配到不同的桶中,以及桶内的链表或红黑树结构。 3. **扩容机制**:研究HashMap如何动态调整容量,以及在扩容过程中如何迁移元素。 4. **链表转红黑树**...

    对java基础集合部分(List、HashMap、HashSet、ArrayList等)底层源码的分析与总结

    Java集合框架是Java编程中非常重要的部分,它提供了一种高效、灵活的数据组织方式。本文主要探讨了几个关键...通过对源码的深入分析,我们可以更好地掌握Java集合框架的工作原理,并根据实际需求选择最适合的数据结构。

    java数据结构源码

    源码分析是理解这些数据结构工作原理的关键,可以帮助开发者提升程序性能和代码质量。以下是一些关于Java数据结构的核心知识点: 1. 数组:数组是最基本的数据结构,它在内存中分配连续的空间来存储相同类型的数据...

    飞秋java源码-interviewNote:面试笔记

    源码分析:ArrayList、Vector、LinkedList、HashMap、ConcurrentHashMap、HashSet、LinkedHashSet and LinkedHashMap Java 并发编程  &gt; ​ 线程机制、线程通信、J.U.C组件、JMM、线程安全、锁优化 Java I/O  磁盘...

Global site tag (gtag.js) - Google Analytics