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.图形或者网状结构 接下来的 ...
随机推荐
- requests模拟浏览器请求模块初识
requests模拟浏览器请求模块初识 一.下载 requests模拟浏览器请求模块属于第三方模块 源码下载地址http://docs.python-requests.org/zh_CN/lates ...
- Java&Selenium自动化测试实现页面元素、页面对象及测试代码分离
一.摘要 本篇博文将介绍自动化测试实现页面元素.页面对象及测试代码分离在自动化框架中的实现 二.解析页面元素定位信息 首先,将页面元素与实际的代码分离,首先我们将页面元素定位信息和定位表达式保存在属性 ...
- iView - DatePicker组件神坑,如何处理?
最近使用iView - DatePicker组件时发现一些问题,明明设置是正常的日期时间格式,当需要使用这个时间的时候,页面却显示 Fri Jun 09 2017 12:00:10 GMT+0800 ...
- 如何处理请求返回的二进制数据流转化成xlsx文件?
/* fileName : 文件名 res:二进制流 */ function getOutExcel(fileName, res) { let blob = new Blob([res], { typ ...
- Kafka中的消息是否会丢失和重复消费(转)
在之前的基础上,基本搞清楚了Kafka的机制及如何运用.这里思考一下:Kafka中的消息会不会丢失或重复消费呢?为什么呢? 要确定Kafka的消息是否丢失或重复,从两个方面分析入手:消息发送和消息消费 ...
- 7、DockerFile案例:自定义centos、自定义tomcat、webapps项目发布
1.Base镜像(scratch) Docker Hub 中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的 2.自定义镜像mycentos 1.Hub默认CentOS镜像什 ...
- Codeforces Good Bye 2017 908F F. New Year and Rainbow Roads
题 OvO http://codeforces.com/contest/908/problem/F CF 908F 解 需要注意细节的模拟题. 如果三种颜色都存在,则记每两个相邻的G组成一个段,对每个 ...
- shell爬取斗图网
#!/bin/bash read -p "请输入要爬取的页面数(默认为10):" page_num page_num=${page_num:-} echo $page_num re ...
- pypdf2:下载Americanlife网页生成pdf合并pdf并添加书签
初步熟悉 安装 pip install pypdf2 合并并添加书签 #!/usr/bin/env python3.5 # -*- coding: utf-8 -*- # @Time : 2019/1 ...
- Can't install '*' from pristine store, because no checksum is recorded for this file
svn同步时,提示clean up,但clean up 时提示: Error:Error performing cleanup for 'E:\project\projectProjectIDEA\b ...