博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
LinkedHashMap源代码阅读
阅读量:5917 次
发布时间:2019-06-19

本文共 5489 字,大约阅读时间需要 18 分钟。

LinkedHashMap

LinkedHashMap内部採用了散列表和链表实现Map接口,并能够保证迭代的顺序,和HashMap不同,其内部维护一个指向全部元素的双向链表,其决定了遍历的顺序,一般是元素插入的顺序进行迭代,只是元素又一次插入顺序不会受到影响。

LinkedHashMap提供一个特殊的构造函数,实现了每次迭代返回近期使用的元素,这个特性能够用于构建LRU缓存。

此外removeEldestEntry(Map.Entry)方法能够被子类覆盖用于推断在加入元素的时候什么时候能够删除元素。

LinkedHashMap性能相同受到初始容量和装填因子的影响,对于基本操作(add,contains,remove)在常数时间内,其性能比HashMap略微低。因为须要额外代价维护链表;只是其遍历性能为O(size)高于HashMapO(capacity)。

LinkedHashMap实现

类定义

直接继承了HashMap

public class LinkedHashMap
extends HashMap
implements Map

成员

private transient Entry
header; //用于遍历的双向链表表头/** * The iteration ordering method for this linked hash map: * true: for access-order, false: for insertion-order */private final boolean accessOrder;

Entry内部类继承了HashMap.Entry<K,V>类,添加了两个指针before和after用于维护遍历顺序,实际上Entry有三个指针父类本身有个next指针用于当发生元素冲突时指向的下一个元素。

由此能够看出用于遍历的双向链表直接加在Entry上面,这样有效节约了空间,实际仅仅比HashMap多了2*size个引用+1个头结点空间消耗。

before和after这两个引用在外部类调用put或remove时,调用其相关方法进行维护(recordAccess和recordRemoval等)。

private static class Entry
extends HashMap.Entry
{
Entry
before, after; Entry(int hash, K key, V value, HashMap.Entry
next) {
super(hash, key, value, next); } private void remove() {
before.after = after; after.before = before; } //existingEntry之前加入当前节点 private void addBefore(Entry
existingEntry) {
after = existingEntry; before = existingEntry.before; before.after = this; after.before = this; } //由父类HashMap的put方法调用,若是acessOrder。则加入到双向链表的头结点后面;本身get方法也会触发调用 void recordAccess(HashMap
m) { LinkedHashMap
lm = (LinkedHashMap
)m; if (lm.accessOrder) { lm.modCount++; remove(); addBefore(lm.header); } } //有元素删除时,调用该方法 void recordRemoval(HashMap
m) { remove(); } }

put方法是继承自父类HashMap,重写了当须要加入元素时候调用的addEntry方法,相同是在相应桶的链表头结点后面加入。加入完以后不是直接进行resize推断,而是推断是否要删除旧的元素,这种方法默认返回false,用户能够重写这种方法用于确定缓存的淘汰机制。

void addEntry(int hash, K key, V value, int bucketIndex) {
createEntry(hash, key, value, bucketIndex); // Remove eldest entry if instructed, else grow capacity if appropriate Entry
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
old = table[bucketIndex]; Entry
e = new Entry
(hash, key, value, old); table[bucketIndex] = e; e.addBefore(header); //每次都是在相应桶链表的開始处加入 size++;}protected boolean removeEldestEntry(Map.Entry
eldest) {
return false;}

get方法

public V get(Object key) {
Entry
e = (Entry
)getEntry(key); if (e == null) return null; e.recordAccess(this); //实现LRU return e.value;}//重写父类方法,效率更高O(size)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;}

视图和迭代器

LinkedHashMap相同继承了创建3个集合类视图:键集合、值集合、键值对集合的方法。因为额外维护一个双向链表保证迭代顺序,重写了相关视图的迭代器实现,LinkedHashIterator通过直接迭代链表的header指针来实现指定顺序遍历。

Iterator
newKeyIterator() {
return new KeyIterator(); }Iterator
newValueIterator() {
return new ValueIterator(); }Iterator
> newEntryIterator() {
return new EntryIterator(); }private class KeyIterator extends LinkedHashIterator
{
public K next() {
return nextEntry().getKey(); }}private class ValueIterator extends LinkedHashIterator
{ public V next() { return nextEntry().value; }}private class EntryIterator extends LinkedHashIterator
> { public Map.Entry
next() { return nextEntry(); }}private abstract class LinkedHashIterator
implements Iterator
{ Entry
nextEntry = header.after; Entry
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
nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); if (nextEntry == header) throw new NoSuchElementException(); Entry
e = lastReturned = nextEntry; nextEntry = e.after; return e; }}

总结

  1. LinkedHashMap继承自HashMap。相关基本操作性能略低于HashMap。因为须要额外代价维护链表。其遍历操作是通过操作该双向链表实现,而非内部散列表数组。因此性能为O(size)比HashMapO(capacity)更高。
  2. 支持两种顺序遍历:元素插入顺序(反复put不算)和近期使用优先顺序(调用put和get类似LRU),默认是依照元素插入顺序遍历。通过构造函数传入true能够实现近期使用优先遍历。每次put或get操作时,将该元素直接又一次放置到链表头结点后面来实现近期使用优先遍历。

  3. LinkedHashMap并没有又一次创建一个新的链表来实现顺序遍历,而是在每一个Entry上多加了两个指针来决定遍历顺序。有效节约了空间消耗。

    实际仅仅比HashMap多了2*size个引用+1个头结点空间消耗。

  4. LinkedHashMap支持元素淘汰策略,能够通过重写removeEldestEntry方法。来决定调用put时候是否须要删除旧的元素。LinkedHashMap能够用于实现LRU缓存,并自己定义元素淘汰策略。

转载于:https://www.cnblogs.com/yutingliuyl/p/6866951.html

你可能感兴趣的文章
hdu1075
查看>>
mysql生产环境____主从同步修复案例
查看>>
【Access2007】Access2007的打开方式
查看>>
openerp中后台代码详解
查看>>
c#中hash table的用法(转)
查看>>
JavaSE学习总结第16天_集合框架2
查看>>
超简单微信公众帐号自动回复和天气播报功能应用
查看>>
12.组合(Composition)
查看>>
Python 面向对象 --- 多态
查看>>
Jedis分布式锁实现
查看>>
推荐一位牛人的博客
查看>>
C# Excel导出
查看>>
shader Model之间的比较
查看>>
C#使用 SSL Socket 建立 Client 与 Server 连接
查看>>
ruby之selenium自动化 or ruby爬虫利器-selenium
查看>>
Mac Outlook 2016 无法打开会议室日历
查看>>
倒水问题
查看>>
poj - 1860 Currency Exchange
查看>>
git的使用(win7 64位)
查看>>
通过闭包可以返回局部变量
查看>>