【源代码】LinkedHashMap源代码剖析
注:下面源代码基于jdk1.7.0_11
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的遍历时间不受桶容量的限制,这是它和HashMap的重要差别之中的一个。
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
public LinkedHashMap() {
super();
accessOrder = false;
}
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super(m);
accessOrder = false;
}
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
... ...
init();
}
这事实上就是所谓的“钩子”,详细代码由子类实现,假设子类希望每次构造的时候都去做一些特定的初始化操作,能够选择复写init方法。
@Override
void init() {
header = new Entry<>(-1, null, null, null);//初始化双向链表
header.before = header.after = header;//不光是双向链表,还是循环链表
}
private static class Entry<K,V> extends HashMap.Entry<K,V> {
// These fields comprise the doubly linked list used for iteration.
Entry<K,V> before, after;//前驱、后继指针
Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
super(hash, key, value, next);
}
/**
* Removes this entry from the linked list.
*/
private void remove() {
before.after = after;
after.before = before;
}
/**
* Inserts this entry before the specified existing entry in the list.
*/
private void addBefore(Entry<K,V> existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
/**
* This method is invoked by the superclass whenever the value
* of a pre-existing entry is read by Map.get or modified by Map.set.
* If the enclosing Map is access-ordered, it moves the entry
* to the end of the list; otherwise, it does nothing.
*/
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();
}
}
public V put(K key, V value) {//HashMap的put方法
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);//发生覆盖操作时。会调用此方法
return oldValue;
}
}
... ...
}
此外,在LinkedHashMap的get方法中,也会调用此方法:
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;
}
观察该方法的逻辑:假设accessOrder为true,那么会调用addBefore方法将当前Entry放到双向链表的尾部,终于在我们遍历链表的时候就会发现最近最少使用的结点的都集中在链表头部(从最近訪问最少到最近訪问最多的顺序)。这就是LRU。
void addEntry(int hash, K key, V value, int bucketIndex) {
super.addEntry(hash, key, value, bucketIndex);
// Remove eldest entry if instructed
Entry<K,V> eldest = header.after;//标记最少訪问的对象
if (removeEldestEntry(eldest)) {//推断是否须要删除这个对象---->可由子类实现来提供缓存功能
removeEntryForKey(eldest.key);
}
}
void createEntry(int hash, K key, V value, int bucketIndex) {
HashMap.Entry<K,V> old = table[bucketIndex];
Entry<K,V> e = new Entry<>(hash, key, value, old);
table[bucketIndex] = e;
e.addBefore(header);//加入到链表尾部
size++;
}
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;
return e;
}
}
【源代码】LinkedHashMap源代码剖析的更多相关文章
- 【Java集合源代码剖析】LinkedHashmap源代码剖析
转载请注明出处:http://blog.csdn.net/ns_code/article/details/37867985 前言:有网友建议分析下LinkedHashMap的源代码.于是花了一晚上时间 ...
- LinkedHashMap源代码阅读
LinkedHashMap LinkedHashMap内部採用了散列表和链表实现Map接口,并能够保证迭代的顺序,和HashMap不同,其内部维护一个指向全部元素的双向链表,其决定了遍历的顺序,一般是 ...
- 图像库---Image Datasets---OpenSift源代码---openSurf源代码
1.Computer Vision Datasets on the web http://www.cvpapers.com/datasets.html 2.Dataset Reference http ...
- MINA2 源代码学习--源代码结构梳理
一.mina总体框架与案例: 1.总体结构图: 简述:以上是一张来自网上比較经典的图,总体上揭示了mina的结构,当中IoService包括clientIoConnector和服务端IoAccepto ...
- 【Java集合源代码剖析】TreeMap源代码剖析
转载请注明出处:http://blog.csdn.net/ns_code/article/details/36421085 前言 本文不打算延续前几篇的风格(对全部的源代码加入凝视),由于要理解透Tr ...
- Java源代码之LinkedHashMap
Java源代码之LinkedHashMap 转载请注明出处:http://blog.csdn.net/itismelzp/article/details/50554412 一.LinkedHashMa ...
- 【源代码】LruCache源代码剖析
上一篇分析了LinkedHashMap源代码,这个Map集合除了拥有HashMap的大部分特性之外.还拥有链表的特点,即能够保持遍历顺序与插入顺序一致. 另外.当我们将accessOrder设置为tr ...
- Java To CSharp源代码转换
前言 开发环境 客户端:Unity3D开发(C#) 服务器:Java (基于Java7) 日 期:2016年09月 需求说明 部分服务器的部分逻辑功能在客户端实现一遍,可以简单的理解为服务器的部分 ...
- Apple II DOS 源代码发布
加州山景城的计算机历史博物馆不仅仅展示硬件,还展示软件.博物馆此前已发布了著名软件MacPaint .Photoshop和APL的源代码,现在它公开了1978年的Apple II DOS源代码.源代码 ...
随机推荐
- stdcall、cdecl、fastcall、thiscall 、naked call的汇编详解
函数调用规范 当高级语言函数被编译成机器码时,有一个问题就必须解决:因为CPU没有办法知道一个函数调用需要多少个.什么样的参数.即计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者 ...
- iOS 本地化应用程序(NSLocalizedString)
App本地化的需要不用讲大家也都明白,本文将介绍一种简单的方法来实现字符串的本地化. 在不考虑本地化的情况下,我们如果在代码中给一个Button定义title,一般会这样写: btn.titleLab ...
- Flowers(二分水过。。。)
Flowers Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Sub ...
- 非确定有限状态自动机的构建(二)——将CharVal转换为NFA
保留版权,转载注明出处:潘军彪的个人博客(http://blog.csdn.net/panjunbiao/article/details/9378933) 将上下文无关文法读入内存之后,可以将它转换成 ...
- 多模块Maven项目怎样使用javadoc插件生成文档
需求 近期要对一个项目结构例如以下的Maven项目生成JavaDoc文档. Project |-- pom.xml ...
- c语言中scanf()、printf()函数
函数调用scanf(“%d”, &weight) 包含两个参数:“%d” 和&weight.C用逗号来隔开函数调用中的多个参数: 但是printf()和scanf()函数比较特殊,其 ...
- GCD其他实用场景
GCD线程间通信 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); ...
- python groupby
groupby() 将key函数作用于原循环器的各个元素.根据key函数结果,将拥有相同函数结果的元素分到一个新的循环器.每个新的循环器以函数返回结果为标签. 这就好像一群人的身高作为循环器.我们可以 ...
- iTextSharp - 建立PDF文件
原文 iTextSharp - 建立PDF文件 01 using iTextSharp.text; 02 using iTextSharp.text.pdf; 03 ... 04 private vo ...
- 软件project师的属性与发展
工作近十年了.[软件project师] 一直是我职业 title 的中心词,仅仅是前面的修饰语在不断变化,从0基础.中级.高级到资深. 事实上 [软件project师] 是一个非常泛化的定义.工作现实 ...