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.图形或者网状结构 接下来的 ...
随机推荐
- spring 的自动定时任务
spring的自动定时任务有两种 第一种:通过xml配置来设置 需要在xml中引入新的约束,并且需要配置<task:scheduled-tasks> ,主要配置内容如下: <?xml ...
- P1582 倒水 题解
来水一发水题.. 题目链接. 正解开始: 首先,我们根据题意,可以得知这是一个有关二进制的题目: 具体什么关系,怎么做,我们来具体分析: 对于每个n,我们尝试将其二进制分解,也就是100101之类的形 ...
- Google 的Web开发相关工具
一.PageSpeed Insights PageSpeed Insights 能够针对移动设备和桌面设备生成网页的实际性能报告,并能够提供关于如何改进相应网页的建议. 在线工具:https://de ...
- CCPC 2017 哈尔滨 D. X-Men && HDU 6233(思维+期望)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6233 题意:一个树上有m个人,每个人在某个节点上,每个时刻每个人可以和一个与他距离大于 1 的点进行交 ...
- hybird(h5)页面自动化测试
- andSelf() V1.2 加入先前所选的加入当前元素中
andSelf() V1.2概述 加入先前所选的加入当前元素中 对于筛选或查找后的元素,要加入先前所选元素时将会很有用.直线电机生产厂家 从jQuery1.8开始,.andSelf()方法已经被标注过 ...
- B/S上传大文件的解决方案
第一点:Java代码实现文件上传 FormFile file = manform.getFile(); String newfileName = null; String newpathname = ...
- 前端vue的get和post请求
vue的get和post需要两个文件vue.js和vue-resource.js 以下是实现的代码,可以参考一下,需要注意的接口的请求需要考虑跨域的问题,其次就是访问页面需要在tomcat下访问,否则 ...
- Towers of Hanoi Strike Back (URAL 2029)
Problem The Tower of Hanoi puzzle was invented by French mathematician Édouard Lucas in the second h ...
- javaweb和数据库的简易商城系统
这是一个基于Javaweb和数据库的简易商城系统.为大二夏季小学期完成. 目录结构 主要功能截图为: 一.购买用户 1.首页(除此界面其余界面访问需要登录才能进入) 查看商品 添加购物车 查看购物车 ...