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的更多相关文章

  1. 学习JDK1.8集合源码之--LinkedHashSet

    1. LinkedHashSet简介 LinkedHashSet继承自HashSet,故拥有HashSet的全部API,LinkedHashSet内部实现简单,核心参数和方法都继承自HashSet,只 ...

  2. 【JDK1.8】JDK1.8集合源码阅读——LinkedHashMap

    一.前言 在上一篇随笔中,我们分析了HashMap的源码,里面涉及到了3个钩子函数,用来预设给子类--LinkedHashMap的调用,所以趁热打铁,今天我们来一起看一下它的源码吧. 二.Linked ...

  3. 学习JDK1.8集合源码之--HashMap

    1. HashMap简介 HashMap是一种key-value结构存储数据的集合,是map集合的经典哈希实现. HashMap允许存储null键和null值,但null键最多只能有一个(HashSe ...

  4. 学习JDK1.8集合源码之--HashSet

    1. HashSet简介 HashSet是一个不可重复的无序集合,底层由HashMap实现存储,故HashSet是非线程安全的,由于HashSet使用HashMap的Key来存储元素,而HashMap ...

  5. 学习JDK1.8集合源码之--ArrayList

    参考文档: https://cloud.tencent.com/developer/article/1145014 https://segmentfault.com/a/119000001857894 ...

  6. 学习JDK1.8集合源码之--TreeMap

    1. TreeMap简介 TreeMap继承自AbstractMap,实现了NavigableMap.Cloneable.java.io.Serializable接口.所以TreeMap也是一个key ...

  7. 学习JDK1.8集合源码之--WeakHashMap

    1. WeakHashMap简介 WeakHashMap继承自AbstractMap,实现了Map接口. 和HashMap一样,WeakHashMap也是一种以key-value键值对的形式进行数据的 ...

  8. 学习JDK1.8集合源码之--ArrayDeque

    1. ArrayDeque简介 ArrayDeque是基于数组实现的一种双端队列,既可以当成普通的队列用(先进先出),也可以当成栈来用(后进先出),故ArrayDeque完全可以代替Stack,Arr ...

  9. 学习JDK1.8集合源码之--Vector

    1. Vector简介 Vector是JDK1.0版本就推出的一个类,和ArrayList一样,继承自AbstractList,实现了List.RandomAccess.Cloneable.java. ...

随机推荐

  1. C++模拟实现Objective-C协议和代理模式

    Objective-C的协议和代理是运用最多的特性之一,可以说在苹果系列开发中无处不在.事实上很多人都不知道其背后的原理.事实上简单点说,这就是设计模式中的代理模式的经典运用.代理模式简单点说就是为其 ...

  2. markdown常用知识点

    为什么要用markdown写开发文档? 1.可以在git上在线预览,docx文档需要下载才能看见: 2. .md文档每次修改之后能被git管理,可追踪修改内容和修改人,但是docx不能追踪修改内容. ...

  3. 微信小程序chooseImage(从本地相册选择图片或使用相机拍照)

    一.使用API wx.chooseImage(OBJECT) var util = require('../../utils/util.js') Page({ data:{ src:"../ ...

  4. JEECG 4.0 版本发布,JAVA快速开发平台

    JEECG 4.0 版本发布,系统全面优化升级,更快,更稳定!         导读                               ⊙平台性能优化,系统更稳定,速度闪电般提升      ...

  5. IDEA本地SBT项目上传到SVN

    需求 将本地创建的一个项目上到SVN 网上很多从SVN下载到idea,提交.更新.删除等操作. 但是少有从本地上传一个项目到svn管理的案例 本文参考https://blog.csdn.net/cao ...

  6. Cesium官方教程11--建模人员必读

    原文地址:https://cesium.com/blog/2014/12/15/gltf-tips-for-artists/ 这篇文章是Branden Coker, an artist from AG ...

  7. Netty设置高低水位

    Configure high and low write watermarks   Server ServerBootstrap bootstrap = new ServerBootstrap(); ...

  8. 手机端META详细解释

    一.天猫 <title>天猫触屏版</title> <meta content="text/html; charset=utf-8" http-equ ...

  9. springmvc-高级参数绑定-映射-异常-json数据交互-拦截器

    1.1. 高级参数绑定 1.1.1. 复制工程 把昨天的springmvc-web工程复制一份,作为今天开发的工程 复制工程,如下图: 粘贴并修改工程名为web2,如下图: 工程右键点击,如下图: 修 ...

  10. elasticsearch query 和 filter 的区别

    Query查询器 与 Filter 过滤器 尽管我们之前已经涉及了查询DSL,然而实际上存在两种DSL:查询DSL(query DSL)和过滤DSL(filter DSL).过滤器(filter)通常 ...