Java集合框架之六----------LinkedHashMap和LinkedHashSet源码分析

本文详细分析了Java集合框架中的LinkedHashMap和LinkedHashSet。LinkedHashMap在HashMap基础上增加了双向链表,确保遍历顺序与插入顺序一致,并支持访问顺序排序。此外,文章还探讨了LinkedHashMap的源码细节,如插入、查找、遍历等操作,以及如何实现LRU缓存。LinkedHashSet则是基于LinkedHashMap,提供了简单的集合包装。
摘要由CSDN通过智能技术生成

1.LinkedHashMap源码分析

1.1 概述

LinkedHashMap 继承自 HashMap,在 HashMap 基础上,通过维护一条双向链表,解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题。除此之外,LinkedHashMap 对访问顺序也提供了相关支持。在一些场景下,该特性很有用,比如缓存。在实现上,LinkedHashMap 很多方法直接继承自 HashMap,仅为维护双向链表覆写了部分方法。

1.2 原理

HashMap的底层结构

LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。其结构可能如下图:

淡蓝色表示前驱,红色箭头表示后继

1.3 源码分析

 

 

1.3.1 Entry的继承体系及特性分析

分析一下键值对节点的继承体系。先来看看继承体系结构图

TreeNode不继承它的一个内部类Node,却继承LinkedHashMap的内部类Entry,个人认为是TreeNode在维持红黑树的时候,还会继续维持链表的结构,而且是双向链表,通过prev和next。

①LinkedHashMap和TreeMap都实现了entry的排序,有什么区别:

--TreeMap按照key排序,而LinkedHashMap按照entry插入或者访问顺序排序

--LinkedHashMap保持entry有序方式是调整链表的before,after指针,而treeMap保持entry有序的方式是对tree结构的调整,因此显然LinkedHashMap代价小

②特殊的构造函数LinkedHashMap(int, float,Boolean)

--boolean = true;迭代器顺序遵循LRU原则,最近最少访问的entry会被最先遍历到,这种map结构非常适合构建LRU缓存

③removeEldestEntry(map.entry)

--通过覆写,可以实现:当添加新的映射到map中时,强制自动移除过期的映射。

--过期数据:

----双链表按插入entry排序,则为最早插入双链表的entry

----双链表按访问entry排序,则为最近最少访问的entry

④和hashmap的比较

--增删改查性能比hashmap要差一些,因为要维护双向链表

--迭代器执行时间长短

----LinkedHashMap和size成比例,HashMap和capacity成比例,因此hashmap相对比较费时,以为size<=capacity

⑤三个特殊回调方法

--afterNodeRemoval,删除节点后,双向链表中unlink

--afterNodeInsertion,插入节点后,是否删除eldest节点

--afterNodeAccess,访问节点后,是否调整当前访问节点的顺序

--这三个方法保证了双向链表的有序性,在hashmap中方法体为空,此处进行覆写

⑥为了清晰理解LHM插入节点后的结构,给出一个例子

--hash函数为:h(key)=key%8

--依次插入元素:(k,v)对依次为:(1,11),(2,12),(3,13),(9,19),(17,27)

--给出结构图:(图中node节点未写出value,只写了key) 

 

1.3.2 属性

保存头指针和尾指针

transient LinkedHashMap.Entry<K,V> head;

transient LinkedHashMap.Entry<K,V> tail;

通过accessOrder来决定双向链表的排序

final boolean accessOrder;

false:构造函数的默认值,表示按照entry的插入顺序进行排序 ,故每插入一个新的entry则添加到双向链表的尾部。(注意:如果插入entry的key之前就存在双向链表中,则此次插入操作只会更改value,不会更改原双向链表各个entry的顺序)

true:表示按entry的访问顺序进行排序,根据LRU原则,最新访问的entry排列在双链表的尾部

1.3.3 构造函数

 

指定初始化容量和扩容负载因子,默认按插入顺序

public LinkedHashMap(int initialCapacity, float loadFactor) {

        super(initialCapacity, loadFactor);

        accessOrder = false;

}

指定初始化容量,默认按插入顺序

public LinkedHashMap(int initialCapacity) {

        super(initialCapacity);

        accessOrder = false;

}

调用父类的无参构造器

public LinkedHashMap() {

        super();

        accessOrder = false;

}

调用父类插入集合的方法putmapentries

public LinkedHashMap(Map<? extends K, ? extends V> m) {

        super();

        accessOrder = false;

        putMapEntries(m, false);

}

初始化容量负载因子,和迭代顺序,false按插入,true按访问

public LinkedHashMap(int initialCapacity,

                         float loadFactor,

                         boolean accessOrder) {

        super(initialCapacity, loadFactor);

        this.accessOrder = accessOrder;

    }

 

1.3.4增加元素

LinkedHashMap并没有重写任何put方法,但是重写了构建新节点的newNode方法。newNode方法会在hashMap中的putVal中被调用,putVal方法会在批量插入数据putMapEntries(Map<? Extends k, ? extends v>, Boolean evict)或者插入单个数据public V put (K key, V value)时候被调用

LinkedHashMap重写了newNode方法,每次构建新节点时,通过linkNodeLast(p);将新节点链接在内部双向链表的尾部,创建了一个以null 为节点的entry

在构建新节点时,构建的是`LinkedHashMap.Entry` 不再是`Node`

Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {

        LinkedHashMap.Entry<K,V> p

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值