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详解,内容包括了linkedhashmap的简介和源码剖析以及关于LinkedHashMap的源码总结,内容丰富,需要的朋友可以参考下。
集合源码分析 编程笔记 学习、总结、记录 ! —— 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集合专题总结:HashMap和HashTable源码...本文总结了HashMap和HashTable的源码学习和面试总结,涵盖了它们的存储结构、构造方法、get和put方法的源码分析、Hash表的特点和缺点、HashTable和HashMap的区别等内容。
源码分析:LinkedHashMap 集合框架 (第 14 篇) 源码分析:TreeMap 集合框架 (第 15 篇) 源码分析:Set 集合 集合框架 (第 16 篇) 源码分析:BlockingQueue 接口 集合框架 (第 17 篇) 源码分析:...
bitset源码Java源码分析 基础集合列表 ArrayList (done) Vector (done) LinkedList (done) Stack (done) ReferenceQueue (done) ArrayDeque (done) Set HashSet (done) TreeSet (done) LinkedHashSet (done) BitSet ...
源码分析:ArrayList、Vector、LinkedList、HashMap、ConcurrentHashMap、HashSet、LinkedHashSet and LinkedHashMap Java 并发编程 > 线程机制、线程通信、J.U.C组件、JMM、线程安全、锁优化 Java I/O 磁盘...
Collections 源码 java Java Java的ArrayList、LinkedList、HashMap、TreeMap、LinkedHashMap、HashSet、TreeSet相关源码分析,及相关问题和应用总结。
概念: LruCache 什么是LruCache? LruCache实现原理是什么? 这两个问题其实可以作为一个问题来回答,...LruCache源码分析 public class LruCache<K> { //缓存 map 集合,为什么要用LinkedHashMap //因为没错取了
源码分析:ArrayList、Vector、CopyOnWriteArrayList、LinkedList、HashMap、ConcurrentHashMap、LinkedHashMap、WeekHashMap。 线程使用方式、两种互斥同步方法、线程协作、JUC、线程安全、内存模型、锁优化。 运行...
java jdk1.8 源码 Java-source-reading 缓慢更新一些个人学习java相关源码过程中的笔记,在这里你将不可避免地看到以下情况: 个别不懂/没想好的地方留空待补全 限于个人水平出现的解读错误 ...LinkedHashMap
源码分析 ArrayList 是一个基于动态数组实现的 List 实现类。它实现了 RandomAccess 接口,因此支持随机访问。ArrayList 的默认大小为 10,添加元素时使用 ensureCapacityInternal() 方法来保证容量足够,如果不够...
11.4.2 实现类LinkedHashMap285 11.4.3 实现类TreeMap286 11.4.4 实现类Properties287 11.5 Collections类288 11.6 泛型概述292 11.7 本章习题300 第12章 12.1 理解线程304 12.1.1 什么是多线程304 12.1.2 进程和...