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的源码进行了详细的分析。LinkedHashMap是继承自HashMap的,它重新写了一个Entry,在原来...
Java集合框架源码分析之LinkedHashMap详解 Java集合框架中的LinkedHashMap是HashMap的子类,它继承了HashMap的存储结构,但引入了一个双向链表的头结点,将所有put到LinkedHashMap的节点连接成一个双向循环链表,...
ArrayList核心源码+扩容机制分析LinkedList核心源码分析HashMap核心源码+底层数据结构分析ConcurrentHashMap核心源码+底层数据结构分析LinkedHashMap核心源码分析CopyOnWriteArrayList核心源码分析...
项目相关 项目介绍 使用建议 贡献指南 常见问题 Java 基础 知识点/面试题总结 : (必看 ): Java 基础常见知识点&面试题总结(上) ...LinkedHashMap 核心源码分析 CopyOnWriteArrayList 核心源码分析
本文将深入剖析Java集合的源码,探讨其内部实现机制,并结合常见面试题,帮助你更好地理解和应用这些知识。 首先,我们从基础开始,Java集合框架主要分为两大类:List(列表)和Set(集合)。List接口包括ArrayList...
这篇源码分析将深入探讨HashMap的工作原理和内部实现。 在HashMap中,每个键值对被封装在一个Entry对象中,这些Entry对象存储在一个数组中。当插入新的键值对时,HashMap会根据键的哈希值计算出数组的索引位置。...
集合源码分析 编程笔记 学习、总结、记录 ! —— 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 12. HashMap在JDK8中的源码分析.avi
JDK1.8源码分析 相关的原始码分析结果会以注解的形式体现到原始码中 已完成部分: ReentrantLock CountDownLatch Semaphore HashMap TreeMap LinkedHashMap ConcurrentHashMap 执行器 ...
总的来说,Java集合框架源码分析可以帮助我们掌握集合操作的底层原理,提高代码性能,增强对并发编程的理解,并且有助于我们设计出更高效、更安全的Java程序。通过对Collections类的深入学习,我们可以更好地利用...
Java集合专题总结:HashMap和HashTable源码...本文总结了HashMap和HashTable的源码学习和面试总结,涵盖了它们的存储结构、构造方法、get和put方法的源码分析、Hash表的特点和缺点、HashTable和HashMap的区别等内容。
通过以上分析,我们可以看到Java 1.8 HashMap的put方法在处理哈希冲突时,不仅使用了链表,还在节点数量达到一定阈值时切换为红黑树,有效提高了数据结构的性能。同时,通过扩容机制,HashMap能够保持较低的哈希冲突...
源码分析:LinkedHashMap 集合框架 (第 14 篇) 源码分析:TreeMap 集合框架 (第 15 篇) 源码分析:Set 集合 集合框架 (第 16 篇) 源码分析:BlockingQueue 接口 集合框架 (第 17 篇) 源码分析:...
本篇文章将主要围绕这些Map的主要实现类进行源码分析,探讨其内部工作原理。 一、HashMap HashMap是最常用的Map实现类,它的核心原理是基于哈希表。HashMap使用一个Entry数组存储键值对,其中Entry是一个内部类,...
2. **源码分析** - **容器实现**:这些类的实现通常包括一个内部存储结构(如数组或链表)和一些基本操作,如添加、删除、查找等。例如,ArrayList的`add()`方法会检查容量并根据需要进行扩容,LinkedList的`remove...
bitset源码Java源码分析 基础集合列表 ArrayList (done) Vector (done) LinkedList (done) Stack (done) ReferenceQueue (done) ArrayDeque (done) Set HashSet (done) TreeSet (done) LinkedHashSet (done) BitSet ...
2. **桶(Bucket)结构**:分析HashMap如何通过哈希值将元素分配到不同的桶中,以及桶内的链表或红黑树结构。 3. **扩容机制**:研究HashMap如何动态调整容量,以及在扩容过程中如何迁移元素。 4. **链表转红黑树**...
Java集合框架是Java编程中非常重要的部分,它提供了一种高效、灵活的数据组织方式。本文主要探讨了几个关键...通过对源码的深入分析,我们可以更好地掌握Java集合框架的工作原理,并根据实际需求选择最适合的数据结构。
源码分析是理解这些数据结构工作原理的关键,可以帮助开发者提升程序性能和代码质量。以下是一些关于Java数据结构的核心知识点: 1. 数组:数组是最基本的数据结构,它在内存中分配连续的空间来存储相同类型的数据...