Java源码初学_LinkedHashMap
一.概述:
LinkedHashMap是HashMap的子类,它的基本操作与HashMap相同,与之不同的是,它可以实现按照插入顺序进行排序.也可以通过在构造函数中指定参数,按照访问顺序排序(LinkedHashSet无法按照访问顺序进行排序).
二.LinkedHashMap是如何实现按照插入顺序进行排序的?
LinkedHashMap在Entry内部类中动了一点小手脚…实际上,LinkedHashMap所有的元素可以构成一个以header(Entry对象)为头的双向链表.在HashMap中有一个钩子方法init,在构造函数最后一行调用,在HashMap的实现中,init方法没有实现任何内容.而LinkedHashMap则实现了init方法,init方法的代码如下:
void init() {
header = new Entry<K,V>(-1, null, null, null);
header.before = header.after = header;
}
LinkedHashMap对addEntry方法也做出了修改,首先它没有改变数组的数据结构,在放入一个元素的时候,依然将元素的hash值对应的索引处的引用指向该元素,将该元素的next属性指向之前该索引处引用指向的元素,不同的是,在插入一个新的元素的时候,它还维护了双向链表数据结构的插入.首先看看内部类Entry的数据结构:
private static class Entry<K,V> extends HashMap.Entry<K,V> {
// 这两个引用说明entry之间是用双向链表连接起来的
Entry<K,V> before, after;
Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
super(hash, key, value, next);
}
/**
* 将这个entry从双向链表中移除
*/
private void remove() {
before.after = after;
after.before = before;
}
/**
* 将这个entry加到给定参数entry的前面
*/
private void addBefore(Entry<K,V> existingEntry) {
after = existingEntry;
before = existingEntry.before;
before.after = this;
after.before = this;
}
/**
* 这个方法调用在当支持按照访问次数排序的时候.也是在HashMap中定义,
但没有给出具体方法,在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();
}
}
再看看addEntry方法和createEntry方法:
void addEntry(int hash, K key, V value, int bucketIndex) {
createEntry(hash, key, value, bucketIndex);
// 在LinkedHashMap中,removeEldestEntry方法始终返回false
Entry<K,V> eldest = header.after;
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
} else {
if (size >= threshold)
resize(2 * table.length);
}
}
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++;
}
可以看出调用的addBefore方法实现了维护双向链表的操作,下面列出了向一个没有任何元素的LinkedHashMap中插入元素时,内部发生的事情:
1.首先计算键值对的key值的hash值.根据hash值计算索引.
2.用传入的键值对new出一个Entry对象.由于索引处是null,那么将底层数组该索引处的引用指向该Entry对象
3.将该entry对象插入到header的前面,这时header.before是该entry,header.after也是该entry.
4.增加size,如果size>=threshold,增加容器的容积.
当继续添加元素的时候,新添加的元素都是在添加到header的前面,而header.after始终是第一个添加的元素.
下面是LinkedHashMap定义的迭代器:
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;
}
}
可以看出,迭代器是从header开始,不断调用header.after获取元素.直到指针又重新回到header.因此这样子的迭代顺序配合内部双向链表的数据结构保证了实现了按照插入顺序排序.
三.LinkedHashMap是如何实现按照访问次序排序的?
在LinkedHashMap中,有一个成员变量accessOrder来判断是否需要按照访问顺序排序.accessOrder可以通过,在构造函数中传入true,来指定,必须按照访问顺序排序.所谓的按照访问顺序排序是指,如果一对键值对被访问了,那么就将它移到当前遍历顺序的最后,这样,最不经常访问的键值对会排在前面.
在LinkedHashMap中,通过在get后,调用recordAccess方法,来实现按照访问顺序排序:
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;
}
而recordAccess方法在entry内部类中实现,它是将该该entry移除双向链表,并且将该entry重新加入到header的前面(遍历时将会最后一个遍历),这样就实现了按照访问次序排序.
Java源码初学_LinkedHashMap的更多相关文章
- Java源码初学_AbstractList&AbstractCollection
一.AbstractCollection抽象类:(提供了Collection接口的骨干实现,以减少实现接口所需要的工作) 1.contains方法 contains方法,通过迭代器对于列表的每一个元素 ...
- Java源码初学_HashSet&LinkedHashSet
一.概述 HashSet是建立在HashMap的基础上的,其内部存在指向一个HashMap对象的引用,操作HashSet实际上就是操作HashMap,而HashMap中所有键的值都指向一个叫做Dumm ...
- Java源码初学_HashMap
一.概念 HashMap的实例有两个参数影响其性能:初始容量和加载因子.容量是哈希表中桶的数量,初始容量只是哈希表在创建时的容量.加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度.当哈希表 ...
- Java源码初学_LinkedList
一.LinkedList的内部数据结构 LinkedList底层是一个链表的数据结构,采用的是双向链表,基本的Node数据结构代码如下: private static class Node<E& ...
- Java源码初学_ArrayList
一.ArrayList的构造器和构造方法 在ArrayList中定义了两个空数组,分别对应当指定默认构造方法时候,指向的数组已经给定容量,但是容量等于0的时候,指向的数组.此外在构造函数中传入Coll ...
- 如何阅读Java源码 阅读java的真实体会
刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比 ...
- Android反编译(一)之反编译JAVA源码
Android反编译(一) 之反编译JAVA源码 [目录] 1.工具 2.反编译步骤 3.实例 4.装X技巧 1.工具 1).dex反编译JAR工具 dex2jar http://code.go ...
- 如何阅读Java源码
刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动.源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心. 说到技术基础,我打个比方吧, ...
- Java 源码学习线路————_先JDK工具包集合_再core包,也就是String、StringBuffer等_Java IO类库
http://www.iteye.com/topic/1113732 原则网址 Java源码初接触 如果你进行过一年左右的开发,喜欢用eclipse的debug功能.好了,你现在就有阅读源码的技术基础 ...
随机推荐
- mysql高可用之PXC(Percona XtraDB Cluster)
简介 Percona XtraDB Cluster是MySQL高可用性和可扩展性的解决方案,Percona XtraDB Cluster提供的特性如下: 1).同步复制,事务要么在所有节点提交或不提交 ...
- [HTML]HTML5实现可编辑表格
HTML5实现的简单的可编辑表格 [HTML]代码 <!DOCTYPE html > <html > <head> <meta charset="u ...
- 修改sql2005字段
alter table 表名 add 字段名 数据类型 default 默认值 增加:alter table AdCategory ADD SEOTitleNo varchar(50); 删除:ALT ...
- Linux多线程同步机制
http://blog.163.com/he_junwei/blog/static/19793764620141711130253/ http://blog.csdn.net/h_armony/art ...
- uva 1025,城市的间谍
题目链接:https://uva.onlinejudge.org/external/10/1025.pdf 题意: 地铁是线性的,有n个站,编号(1~n),M1辆从左至右的车,和M2辆从右至左的车,发 ...
- 使用main方法调用http请求本地服务器的某个servlet报错问题
java.io.IOException: Server returned HTTP response code: 500 for URL: http://localhost:8081/test/myS ...
- 触发onSaveInstanceState和onRestoreInstanceState的时机
先看Application Fundamentals上的一段话: Android calls onSaveInstanceState() before the activity becomes ...
- LYK 与实验室
LYK 与实验室(lab)Time Limit:5000ms Memory Limit:64MB[题目描述] LYK 在一幢大楼里,这幢大楼共有 n 层,LYK 初始时在第 a 层上.这幢大楼有一个秘 ...
- 数据库连接池系列之——c3p0
c3p0的jar包是:c3p0-0.9.1.jar <bean id = "dataSource" class = "com.mchange.v2.c3p0.Com ...
- JavaBean基础转载
JavaWeb:JavaBean基础 JavaBean基础 JavaBean简介: 1.JavaBean是一种可以重复使用的类,可以没有用户界面,主要负责业务数据或者处理事物(数据运算.操作数据库) ...