您的位置:首页 > 编程语言 > Java开发

java源码解读之LinkedHashMap------jdk 1.7

2017-04-12 15:45 477 查看
前面分析了HashMap的实现,我们知道其底层数据存储是一个hash表(数组+单向链表)。接下来我们看一下另一个LinkedHashMap,它是HashMap的一个子类,他在HashMap的基础上维持了一个双向链表(hash表+双向链表),在遍历的时候可以使用插入顺序(先进先出,类似于FIFO),或者是最近最少使用(LRU)的顺序。

来具体看下LinkedHashMap的实现。

1.定义


public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>


2. 属性


//其他属性从hashmap处继承
//头结点
private transient Entry<K,V> header;
//引用自网上:当accessOrder为true时表示LinkedHashMap内部节点按照访问次数排序,最老的节点也就是访问最少的节点.当 accessOrder为false时表示LinkedHashMap内部节点按照插入顺序排序,最老的节点也就是最早插入的节点,该值默认为false.
private final boolean accessOrder;

//其他基本一致,来看看entry是怎么重写的才能实现双向链表
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;
}
//在hashmap调用put相同key的时候,get,putForNullKey的时候调用
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
//如果开启算法排序
if (lm.accessOrder) {
//修改次数+1
lm.modCount++;
//先删除再添加,进行排序
//删除当前节点
remove();
//把当前元素加入到header前
addBefore(lm.header);
}
}
//删除当前元素
void recordRemoval(HashMap<K,V> m) {
remove();
}
}


3. 构造器


//自己设定临界值和负载因子
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
//自己设置临界值
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
//使用默认属性
public LinkedHashMap() {
super();
accessOrder = false;
}
//根据传入的map集合创建LinkedHashMap
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;
}


4. 解析部分方法源码

4.1 解析部分源码
LinkedHashMap是继承hashmap的,所以增删改查方法基本一致,只是重写了其中几个方法


void transfer(HashMap.Entry[] newTable, boolean rehash) {
int newCapacity = newTable.length;
//从hashmap的foreach遍历改为从头结点开始遍历
for (Entry<K,V> e = header.after; e != header; e = e.after) {
//找出应该放置的位置
if (rehash)
e.hash = (e.key == null) ? 0 : hash(e.key);
int index = indexFor(e.hash, newCapacity);
//把newTable[index]原来位置的结点改为当前节点e节点的下一个节点
e.next = newTable[index];
//把newTable[index]赋值为当前节点e
newTable[index] = e;
}
}
void init() {
//创建LinkedHashMap前,先创建头节点
header = new Entry<>(-1, null, null, null);
header.before = header.after = header;
}
public V get(Object key) {
//如果通过key查找不到对应entry,则直接返回null
Entry<K,V> e = (Entry<K,V>)getEntry(key);
if (e == null)
return null;
//与hashmap最大的不同,如果开启了算法排序,则对entry进行排序
e.recordAccess(this);
return e.value;
}
public void clear() {
super.clear();
//LinkedHashMap与hashmap不同的清空之处在于他有一个头节点的前后关系需要清理
header.before = header.after = header;
}
void addEntry(int hash, K key, V value, int bucketIndex) {
super.addEntry(hash, key, value, bucketIndex);
//判断是否需要删除头结点,因为header不存元素,所以是header的下一个元素,默认是false
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++;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: