学习JDK1.8集合源码之--LinkedHashMap
1. LinkedHashMap简介
LinkedHashMap继承自HashMap,实现了Map接口。
LinkedHashMap是HashMap的一种有序实现(多态,HashMap的有序态),可以说是HashMap的一种拓展,弥补了HashMap无序这一缺点,但它实现有序的代价是牺牲了时间和空间上的开销。
LinkedHashMap的有序是通过维护一条双向链表保证了元素的有序性,除了有序这一点之外,LinkedHashMap和HashMap差不多,也就没有太多需要描述的了。
2. LinkedHashMap实现
1. 核心参数
//内部Entry实体,继承自HashMap.Node
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);
}
} //头节点
transient LinkedHashMap.Entry<K,V> head; //尾节点
transient LinkedHashMap.Entry<K,V> tail; //排序方式,true:访问顺序,false:插入顺序
final boolean accessOrder;
从上面可以看出,LinkedHashMap核心属性都是继承自HashMap,只是在HashMap的基础上增加了前后节点来维护一个双向链表,head和tail为链表的头尾节点,而且它的排序方式有两种:访问顺序和插入顺序。
2. 构造函数
//无参构造,构造一个初始容量16、加载因子0.75的LinkedHashMap,默认按照插入顺序排序
public LinkedHashMap() {
super();
accessOrder = false;
} //构造一个指定初始容量、加载因子0.75的LinkedHashMap,默认按照插入顺序排序
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
} //构造一个指定初始容量、指定加载因子的LinkedHashMap,默认按照插入顺序排序
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
} //构造一个指定初始容量、指定加载因子的LinkedHashMap,指定顺序方式
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
} //构建一个能够容纳传入map中元素的最小2次幂的初始容量(最小16)、加载因子0.75的LinkedHashMap,默认按照插入顺序排序
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super();
accessOrder = false;
putMapEntries(m, false);
}
LinkedHashMap的构造也是基于HashMap进行构造的,只是在构造时加了排序方式的设置。
3. 核心方法
LinkedHashMap的实现基本上调用HashMap的API实现存储、获取、删除、扩容等操作,只是在这些操作的基础上增加了一些维护双向链表的逻辑,保证了有序性。如果是按照插入顺序的话,每次调用newNode的时候都会调用linkNodeLast来把新建的节点放到链表尾部,如果是按照访问顺序排序的话,则增加了修改和获取节点时调用afterNodeAccess将该节点放到链表尾部。
//将p节点放到链表尾部
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
//取LinkedHashMap的尾节点赋值给last
LinkedHashMap.Entry<K,V> last = tail;
//将尾节点设为p
tail = p;
//如果不存在尾节点,说明链表是空的,把链表头部也设为p
if (last == null)
head = p;
else {
//否则则将p设为last的下一节点,last设为p的上一节点
p.before = last;
last.after = p;
}
} //将节点src替换为dst节点
private void transferLinks(LinkedHashMap.Entry<K,V> src,
LinkedHashMap.Entry<K,V> dst) {
//将src的上一个节点设为dst的上一个节点并赋值给b
LinkedHashMap.Entry<K,V> b = dst.before = src.before;
//将src的下一个节点设为dst的下一个节点并赋值给a
LinkedHashMap.Entry<K,V> a = dst.after = src.after;
//如果b==null,说明src就是头节点,将dst设为头节点
if (b == null)
head = dst;
//否则将b的下一节点设为dst
else
b.after = dst;
//如果哦a==null,说明src就是尾节点,将dst设为尾节点
if (a == null)
tail = dst;
//否则则将a的上一节点设为dst
else
a.before = dst;
} /**下面的方法都是重写HashMap到的方法**/ //重置LinkedHashMap
void reinitialize() {
super.reinitialize();
head = tail = null;
} //HashMap中的untreeify时调用,将树形节点替换成普通节点
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;
} //新建一个树形节点并追加到LinkedHashMap尾部,调用父类的TreeNode
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;
} //HashMap中的treeifyBin时调用,将普通节点替换成树形节点
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;
} //HashMap中移除元素后调用,调整LinkedHashMap的链表结构
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中添加元素后调用,evict为false说明底层HashMap在初始化模式
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
//removeEldestEntry方法直接返回false,没有具体实现,所以这个方法是用来给子类重写的
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
} //访问一个元素后调用,包括put、replace、get等操作
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;
}
} //判断LinkedHashMap中是否包含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
public V get(Object key) {
Node<K,V> e;
//通过HashMap的getNode方法获取节点
if ((e = getNode(hash(key), key)) == null)
return null;
//如果是按照访问顺序排序,则将该元素放到链表尾部
if (accessOrder)
afterNodeAccess(e);
return e.value;
} //根据key获取value,与get不同的是key不存在时会返回指定的默认值
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;
} //清空LinkedHashMap
public void clear() {
//调用父类的清空方法
super.clear();
//将链表首尾节点都置空
head = tail = null;
} //返回key的有序set集合
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new LinkedKeySet();
keySet = ks;
}
return ks;
} //返回value的有序集合
public Collection<V> values() {
Collection<V> vs = values;
if (vs == null) {
vs = new LinkedValues();
values = vs;
}
return vs;
} //返回key-value实体的有序set集合
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new LinkedEntrySet()) : es;
}
学习JDK1.8集合源码之--LinkedHashMap的更多相关文章
- 学习JDK1.8集合源码之--LinkedHashSet
1. LinkedHashSet简介 LinkedHashSet继承自HashSet,故拥有HashSet的全部API,LinkedHashSet内部实现简单,核心参数和方法都继承自HashSet,只 ...
- 【JDK1.8】JDK1.8集合源码阅读——LinkedHashMap
一.前言 在上一篇随笔中,我们分析了HashMap的源码,里面涉及到了3个钩子函数,用来预设给子类--LinkedHashMap的调用,所以趁热打铁,今天我们来一起看一下它的源码吧. 二.Linked ...
- 学习JDK1.8集合源码之--HashMap
1. HashMap简介 HashMap是一种key-value结构存储数据的集合,是map集合的经典哈希实现. HashMap允许存储null键和null值,但null键最多只能有一个(HashSe ...
- 学习JDK1.8集合源码之--HashSet
1. HashSet简介 HashSet是一个不可重复的无序集合,底层由HashMap实现存储,故HashSet是非线程安全的,由于HashSet使用HashMap的Key来存储元素,而HashMap ...
- 学习JDK1.8集合源码之--ArrayList
参考文档: https://cloud.tencent.com/developer/article/1145014 https://segmentfault.com/a/119000001857894 ...
- 学习JDK1.8集合源码之--TreeMap
1. TreeMap简介 TreeMap继承自AbstractMap,实现了NavigableMap.Cloneable.java.io.Serializable接口.所以TreeMap也是一个key ...
- 学习JDK1.8集合源码之--WeakHashMap
1. WeakHashMap简介 WeakHashMap继承自AbstractMap,实现了Map接口. 和HashMap一样,WeakHashMap也是一种以key-value键值对的形式进行数据的 ...
- 学习JDK1.8集合源码之--ArrayDeque
1. ArrayDeque简介 ArrayDeque是基于数组实现的一种双端队列,既可以当成普通的队列用(先进先出),也可以当成栈来用(后进先出),故ArrayDeque完全可以代替Stack,Arr ...
- 学习JDK1.8集合源码之--Vector
1. Vector简介 Vector是JDK1.0版本就推出的一个类,和ArrayList一样,继承自AbstractList,实现了List.RandomAccess.Cloneable.java. ...
随机推荐
- GROUP方法也是连贯操作方法之一
GROUP方法也是连贯操作方法之一,通常用于结合合计函数,根据一个或多个列对结果集进行分组 . group方法只有一个参数,并且只能使用字符串. 例如,我们都查询结果按照用户id进行分组统计: $th ...
- csp-s模拟测试55 联,赛,题题解
题面:https://www.cnblogs.com/Juve/articles/11610969.html 联: 用线段树维护区间和,要修改成1或0就线段树修改区间和 如果是异或,那么新的区间和就是 ...
- ssm框架,出现xxx不能加载,或者bean不能加载时的解决方案之一
有可能是你在项目的mapper.xml文件中添加了本项目没有的实体类, 你把日志中找不到的最后一个实体类全项目搜索下,改成本项目可以引用的即可
- slam课程
美国宾夕法尼亚大学最近有录制一套 无人机视觉定位导航相关的视频课程,2019年3月份在YouTube上更新完毕,质量非常高,名字叫Robotics,视频课程列表:https://www.youtube ...
- l洛谷 NOIP提高组模拟赛 Day2
传送门 ## T1 区间修改+单点查询.差分树状数组. #include<iostream> #include<cstdio> #include<cstring> ...
- linux 最新化安装后安卓 KDE 桌面
yum -y install epel-releaseyum -y groupinstall "X Window System"yum -y groupinstall " ...
- centos 以太坊多节点私链搭建
环境 centos 7 搭建 3 个节点的 私链. 第一步 安装 一些依赖的 工具 yum update -y && yum install git wget bzip2 vim ...
- RvmTranslator7.2
1. RvmTranslator7.2 增加一个视图方块,方便视图切换; Download: https://github.com/eryar/RvmTranslator/releases/tag/7 ...
- badboy的录制和jmeter的使用
v Jmeter是什么 Apache Jmeter是Apache组织开发的基于Java的压力测试工具. Jmeter可以用于对服务器.网络或对象模拟巨大的负载,来自不同压力类别下测试它们的强度和分析 ...
- DBUtils(DataSourceUtils提供数据源)
DBUtils是apache组织的一个工具类,jdbc的框架,更方便我们使用 使用步骤: 1.导入jar包(commons-dbutils-1.4.jar,c3p0-0.9.1.2.jar) 1.1导 ...