学习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. ...
随机推荐
- UNION操作用于合并两个或多个 SELECT 语句的结果集。
UNION操作用于合并两个或多个 SELECT 语句的结果集. 大理石平台价格 使用示例: $Model->field('name') ->table('think_user_0') -& ...
- Python-进程(1)
目录 操作系统发展史 穿孔卡片 联机批处理系统 统计批处理系统 单道 多道技术 空间上复用 时间上复用 并行与并发 进程 程序与进程 进程调度 进程的三个状态 就绪态 运行态 阻塞态 同步和异步 阻塞 ...
- LUOGU P2831 愤怒的小鸟 (NOIP 2016)
题面 题解 好像昨天wxl大爷讲的是O(Tn*2^n)的做法,后来没想通,就自己写了个O(Tn^2*2^n)的暴力状压, 莫名其妙过了??数量级二十亿??懵逼,可能到了CCF老爷机上就T了.dp[S] ...
- 微信开发SDK支持小程序 ,Jeewx-Api 1.3.1 版本发布
JEEWX-API 是一款JAVA版的微信开发SDK,支持微信公众号.小程序.微信企业号.支付宝生活号SDK和微博SDK.你可以基于她 快速的傻瓜化的进行微信开发.支付窗开发和微博开发. 基于jeew ...
- 11-2-break
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 史上最贵域名诞生!360斥资1700万美元买360.com
昨日,360公司官方人士向腾讯科技确认,公司已斥巨资收购国际顶级域名360.com.传闻这一收购价格为1700万美元,约合人民币1.1亿元. 史上最贵域名诞生!360斥资1700万美元买360.com ...
- 深度残差网络——ResNet学习笔记
深度残差网络—ResNet总结 写于:2019.03.15—大连理工大学 论文名称:Deep Residual Learning for Image Recognition 作者:微软亚洲研究院的何凯 ...
- THUWC 游记
考试前的一个周末 PKUWC没过,去不了,自闭,我死了. 考试前的星期一 THUWC居然过了!!!大恩大德永世难忘,我又活了. 考试前的周四 WTF!??为什么要用Ubuntu,我完全不会,凉了凉了, ...
- leetcode 75 Sorted Colors
两种解法 1)记录0和1的个数 然后按照记录的个数将0和1重新放入原数组,剩下的补2 2)双指针left,right left表示0~left-1都为0,即i之前都为0 right表示right+1~ ...
- 组件:组合slot
<!DOCTYPE html> <html lang="zh"> <head> <title></title> < ...