java数据结构之LinkedHashMap
一、LinkedHashMap源码注释
public class LinkedHashMap<K,V>
extends HashMap<K,V>
implements Map<K,V>
{ /**
* LinkedHashMap的节点类,在HashMap的节点的基础上增加了指向前一个节点和后一个节点的属性,来构成双向链表
*/
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
} private static final long serialVersionUID = 3801124242820219131L; /**
* 双向链表的头节点(链表中最先加入的节点)
*/
transient LinkedHashMap.Entry<K,V> head; /**
* 双向链表的尾节点(链表中最后加入的节点)
*/
transient LinkedHashMap.Entry<K,V> tail; /**
* 用来确定对LinkedHashMap中的节点的迭代顺序,
* 如果accessOrder为true就是按照访问顺序,如果为false就按照插入顺序
*/
final boolean accessOrder; // internal utilities // 将一个节点加入链表尾部
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
tail = p;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
} // 用dst节点替换src节点
private void transferLinks(LinkedHashMap.Entry<K,V> src,
LinkedHashMap.Entry<K,V> dst) {
LinkedHashMap.Entry<K,V> b = dst.before = src.before;
LinkedHashMap.Entry<K,V> a = dst.after = src.after;
if (b == null)
head = dst;
else
b.after = dst;
if (a == null)
tail = dst;
else
a.before = dst;
} // 重新初始化
void reinitialize() {
super.reinitialize();
head = tail = null;
}
//创建一个新的节点,并将节点加到双向链表的结尾。这是对hashMap中的newNode方法的覆盖
//所以在LinkedHashMap中put方法添加节点的同时会将该节点加到双向链表中
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p);
return p;
} //创建并返回一个节点,并在双向链表中用新的节点替换旧的节点
//用在对
Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
LinkedHashMap.Entry<K,V> t =
new LinkedHashMap.Entry<K,V>(q.hash, q.key, q.value, next);
transferLinks(q, t);
return t;
}
//创建一个树结点,并添加到双向链表的尾部
//用于加入将树节点加入Map中时创建节点
TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
linkNodeLast(p);
return p;
}
//创建并返回一个树结点,并用该节点替换原来的节点
TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next);
transferLinks(q, t);
return t;
}
//如果节点e被移除,那么就在双向链表中的解除前后接节点的关系
void afterNodeRemoval(Node<K,V> e) { // unlink
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.before = p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a == null)
tail = b;
else
a.before = b;
}
//覆盖HashMap中回调函数,在加入新的节点后调用,如果要实现添加新的节点后剔除最老的节点可以通过覆盖这个方法来实现
//可以实现LRU缓存策略
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
//如果accessOrder为true,在访问过某个节点之后,将该节点放到最后的位置,相当于作为最新的节点
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
//仅从writeObject调用,以确保顺序一致。
void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
s.writeObject(e.key);
s.writeObject(e.value);
}
} /**
* 指定初始容量和加载因子的构造函数,并且指定迭代顺序文为插入顺序
*/
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
} /**
* 指定初始容量和加载因子0.75的构造函数,并且指定迭代顺序文为插入顺序
*/
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
} /**
* 使用默认初始容量16和加载因子0.75的构造函数,并且指定迭代顺序文为插入顺序
*/
public LinkedHashMap() {
super();
accessOrder = false;
} /**
* 使用默认初始容量16和加载因子0.75的构造函数,并且指定迭代顺序文为插入顺序
* 将m中的键值对加入新的LinkedHashMap中
*/
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super();
accessOrder = false;
putMapEntries(m, false);
} /**
* 指定初始容量、加载因子以及迭代顺序的构造函数
*/
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
} /**
* 查询是否包含value值
*/
public boolean containsValue(Object value) {
for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
V v = e.value;
if (v == value || (value != null && value.equals(v)))
return true;
}
return false;
} /**
* 通过key来获取value,如果accessOrder为true就将该键值对放在最后面
*/
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e);
return e.value;
} /**
* 通过key查找值,如果没有值就返回defaultValue
*/
public V getOrDefault(Object key, V defaultValue) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return defaultValue;
if (accessOrder)
afterNodeAccess(e);
return e.value;
} /**
* 清空整个Map,将头节点和尾节点都设置为null
*/
public void clear() {
super.clear();
head = tail = null;
} /**
* 是否允许删除最老的节点
* 通过覆盖这个方法可以实现第一固定大小的缓存集合,当集合中的元素个数大于多少时删除最老的元素
*/
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
} /**
* 返回LinkedHashMap的key的集合视图
*/
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new LinkedKeySet();
keySet = ks;
}
return ks;
}
//LinkedHashMap key集合视图,可以对LinkedHashMap中的key进行迭代
// LinkedHashMap和LinkedKeySet会相互影响
final class LinkedKeySet extends AbstractSet<K> {
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator<K> iterator() {
return new LinkedKeyIterator();
}
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator<K> spliterator() {
return Spliterators.spliterator(this, Spliterator.SIZED |
Spliterator.ORDERED |
Spliterator.DISTINCT);
}
public final void forEach(Consumer<? super K> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
action.accept(e.key);
if (modCount != mc)
throw new ConcurrentModificationException();
}
} /**
* LinkedHashMap的value集合视图,存有LinkedHashMap中的所有value值,
* Map的值变动会影响到values这个集合中的值
*/
public Collection<V> values() {
Collection<V> vs = values;
if (vs == null) {
vs = new LinkedValues();
values = vs;
}
return vs;
}
//LinkedHashMap的value集合视图
final class LinkedValues extends AbstractCollection<V> {
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator<V> iterator() {
return new LinkedValueIterator();
}
public final boolean contains(Object o) { return containsValue(o); }
public final Spliterator<V> spliterator() {
return Spliterators.spliterator(this, Spliterator.SIZED |
Spliterator.ORDERED);
}
public final void forEach(Consumer<? super V> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
action.accept(e.value);
if (modCount != mc)
throw new ConcurrentModificationException();
}
} /**
* LinkedHashMap键值对集合视图
* LinkedHashMap和LinkedEntrySet的变动也是会相互影响的
*/
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
} final class LinkedEntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size() { return size; }
public final void clear() { LinkedHashMap.this.clear(); }
public final Iterator<Map.Entry<K,V>> iterator() {
return new LinkedEntryIterator();
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Node<K,V> candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
public final Spliterator<Map.Entry<K,V>> spliterator() {
return Spliterators.spliterator(this, Spliterator.SIZED |
Spliterator.ORDERED |
Spliterator.DISTINCT);
}
public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
action.accept(e);
if (modCount != mc)
throw new ConcurrentModificationException();
}
} // 重写map的方法 //遍历LinkedHashMap中的键值对,然后做相关的操作
public void forEach(BiConsumer<? super K, ? super V> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
action.accept(e.key, e.value);
if (modCount != mc)
throw new ConcurrentModificationException();
}
//遍历LinkedHashMap中的键值对,并计算出新的value
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
if (function == null)
throw new NullPointerException();
int mc = modCount;
for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
e.value = function.apply(e.key, e.value);
if (modCount != mc)
throw new ConcurrentModificationException();
} // Iterators迭代器 abstract class LinkedHashIterator {
LinkedHashMap.Entry<K,V> next;
LinkedHashMap.Entry<K,V> current;
int expectedModCount; LinkedHashIterator() {
next = head;
expectedModCount = modCount;
current = null;
} public final boolean hasNext() {
return next != null;
} final LinkedHashMap.Entry<K,V> nextNode() {
LinkedHashMap.Entry<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
current = e;
next = e.after;
return e;
} public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
expectedModCount = modCount;
}
}
//key迭代器
final class LinkedKeyIterator extends LinkedHashIterator
implements Iterator<K> {
public final K next() { return nextNode().getKey(); }
}
//value迭代器
final class LinkedValueIterator extends LinkedHashIterator
implements Iterator<V> {
public final V next() { return nextNode().value; }
}
//键值对迭代器
final class LinkedEntryIterator extends LinkedHashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
} }
二、LinkedHashMap源码解释
1、LinkedHashMap继承了HashMap,其方法和HashMap大体上差不多。但是LinkedHashMap是有序的,所以LinkedHashMap的节点在HashMap的基础上新增了before和after来分别指向前一个节点和后一个节点。在结构上构成双向链表,来实现有序。
2、LinkedHashMap通过accessOrder字段来确定集合中的节点的排序。如果accessOrder == false 那么就按照添加的先后顺序来进行排序,先加入的排在前面;如果accessOrder == true 那么就通过访问的顺序来进行排序,最近访问的排在后面。
三、LRU算法及其实现
1、 LRU是Least Recently Used的缩写,即最近最少使用。该算法基于的思想是如果一个数据最近没有被使用,那它将来被使用到的概率会比最近使用过的数据概率小。这样我们在进行缓存的时候在空间不足时就剔除一些较少使用的数据,来达到释放空间的目的。
2、通过LinkedHashMap中的accessOrder 字段可知,若将构建LinkedHashMap时将该字段设置为true,那么其内部将通过访问顺序来进行排序。这样我们就能实现LRU算法
3、使用LinkedHashMap来实现LRU算法具体代码
//继承LinkedHashMap来实现LRU缓存
public class LRULinkedHashMap<K,V> extends LinkedHashMap<K,V>{ private static final long serialVersionUID = 7164699172329853931L; //最多缓存的个数
private int limitCapacity; public LRULinkedHashMap(){
this(1024,0.75f);
}
//指定缓存个数
public LRULinkedHashMap(int limitCapacity){
this(limitCapacity,0.75f);
} //指定缓存容量大小和加载因子
public LRULinkedHashMap(int limitCapacity, float loadFactor){
//通过计算得到容量的大小,以免发生resize浪费时间和空间
super((int)Math.ceil(limitCapacity/loadFactor) +1,loadFactor,true);
this.limitCapacity = limitCapacity; } //只有当前map中的数据大于limitCapacity时才返回true
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return size() > limitCapacity;
} } //测试
public class TestLRU { public static void main(String[] args) {
LRULinkedHashMap<String,Integer> cacheMap = new LRULinkedHashMap<>(3);
cacheMap.put("1", 1);
cacheMap.put("2", 2);
cacheMap.put("3", 3);
cacheMap.put("4", 4);
cacheMap.put("5", 6);
cacheMap.put("1", 2);
cacheMap.get("4");
System.out.println(cacheMap);
} }
java数据结构之LinkedHashMap的更多相关文章
- Java数据结构和算法(四)赫夫曼树
Java数据结构和算法(四)赫夫曼树 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 赫夫曼树又称为最优二叉树,赫夫曼树的一个 ...
- Java源代码之LinkedHashMap
Java源代码之LinkedHashMap 转载请注明出处:http://blog.csdn.net/itismelzp/article/details/50554412 一.LinkedHashMa ...
- Java TreeMap 和 LinkedHashMap【笔记】
Java TreeMap 和 LinkedHashMap[笔记] TreeMap TreeMap基本结构 TreeMap 底层的数据结构就是红黑树,和 HashMap 的红黑树结构一样 与HashMa ...
- Java数据结构之队列的实现以及队列的应用之----简单生产者消费者应用
Java数据结构之---Queue队列 队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置插入和删除,而队列只允许在 ...
- JAVA数据结构系列 栈
java数据结构系列之栈 手写栈 1.利用链表做出栈,因为栈的特殊,插入删除操作都是在栈顶进行,链表不用担心栈的长度,所以链表再合适不过了,非常好用,不过它在插入和删除元素的时候,速度比数组栈慢,因为 ...
- Java数据结构之树和二叉树(2)
从这里始将要继续进行Java数据结构的相关讲解,Are you ready?Let's go~~ Java中的数据结构模型可以分为一下几部分: 1.线性结构 2.树形结构 3.图形或者网状结构 接下来 ...
- Java数据结构之树和二叉树
从这里开始将要进行Java数据结构的相关讲解,Are you ready?Let's go~~ Java中的数据结构模型可以分为一下几部分: 1.线性结构 2.树形结构 3.图形或者网状结构 接下来的 ...
- Java数据结构之线性表(2)
从这里开始将要进行Java数据结构的相关讲解,Are you ready?Let's go~~ java中的数据结构模型可以分为一下几部分: 1.线性结构 2.树形结构 3.图形或者网状结构 接下来的 ...
- Java数据结构之线性表
从这里开始将要进行Java数据结构的相关讲解,Are you ready?Let's go~~ java中的数据结构模型可以分为一下几部分: 1.线性结构 2.树形结构 3.图形或者网状结构 接下来的 ...
随机推荐
- Vue项目中的文件导出
项目中涉及到文件导出,分xml和excel导出.不同的文件导出格式不同,需要根据文件类型判断导出格式. exportAllData(val){ //全部导出 if(!val){ this.export ...
- 下一代容器技术podman简介
PODMAN主要由红帽发起和推动,是下一代的容器技术,包括如下三个模块:Podman,Skopeo和Buildah这三个工具都是符合OCI计划下的工具(github/containers).主要是由R ...
- 关于STM32的I2C硬件DMA实现
关于STM32的I2C硬件DMA实现 网上看到很多说STM32的I2C很难用,但我觉得还是理解上的问题,STM32的I2C确实很复杂,但只要基础牢靠,并没有想象中的那么困难. 那么就先从基础说起,只说 ...
- MFC 静态文本框
窗体上操作控件内容,需要句柄,在控件处使用鼠标右键——添加变量. DoDataExchange()函数会自动生成代码,把ID与变量绑定(即DDX_Control(pDX, IDC_TEXT, objT ...
- AtCoder Beginner Contest 137 D题【贪心】
[题意]一共有N个任务和M天,一个人一天只能做一个任务,做完任务之后可以在这一天之后的(Ai-1)天拿到Bi的工资,问M天内最多可以拿到多少工资. 链接:https://atcoder.jp/cont ...
- Eclipse 导入项目
- vector 与 array
vector STL中的模板数组(在堆中分配内存空间,通过new delete管理内存) 使用包涵头文件#include <vector> vector<ElmentTpye> ...
- 路由器配置——RIP路由
一.实验目的:用rip路由实现全网互通 二.拓扑图: 三.具体步骤配置 (1)R1路由器配置 Router>enable --进入特权模式Router#configure terminal ...
- [vim]多行注释和多行删除
vim中多行注释和多行删除命令,这些命令也是经常用到的一些小技巧,可以大大提高工作效率. 1.多行注释: 首先按esc进入命令行模式下,按下Ctrl + v,进入列(也叫区块)模式; 在行首使用上下键 ...
- Linux dirname 和 basename
[参考文章]:Linux shell - `dirname $0` 定位到运行脚本的相对位置 [参考文章]:Linux命令之basename使用 1. dirname $0 获取脚本文件所在的目录信息 ...