LinkedHashMap 集合源码分析

news/2024/6/18 22:03:33 标签: java, 集合框架, LinkedHashMap

LinkedHashMap__0">LinkedHashMap 集合源码分析

文章目录

  • LinkedHashMap 集合源码分析
  • 一、字段分析
  • 二、内部类分析
  • 三、构造方法分析
  • 四、内部方法分析
  • 五、总结


在这里插入图片描述

  • LinkedHashMap 是 HashMap 的子类,在 HashMap 的基础上维护了双向链表,保证了有序性。默认是不排序的,可在初始化时传入 accessOrder = true,则进行排序

一、字段分析

java">// 指向LinkedHashMap 维护的双向链表的头结点
transient LinkedHashMap.Entry<K,V> head;
// 指向LinkedHashMap 维护的双向链表的尾结点
transient LinkedHashMap.Entry<K,V> tail;
// 是否排序:默认false:不排序。设为true时:越近访问的节点越靠近尾结点,即头结点 -> 尾结点
// 按 最近访问时间降序排列,即越靠近尾结点,离上次访问时间越近。
final boolean accessOrder;

二、内部类分析

java">//可以看到是继承了 hashmap 的 node,再次基础上多了 before 和  after,就是用来维护双向链表的
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);
        }
    }

三、构造方法分析

java">	//传入默认的初始容量 和 加载因子,默认不排序
    public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }

   //闯入默认的初始容量,默认不排序
    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }

    //无参构造,默认不排序
    public LinkedHashMap() {
        super();
        accessOrder = false;
    }
	
	//传入集合m来,使用集合m的所有元素来构建 LinkedHashMap,默认不排序
    public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }

    //传入初始容量,加载因子,也可指定进行排序即true
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

四、内部方法分析

  • LinkedHashMap 的添加元素、删除元素,扩容等方法都是直接使用 了 HashMap 的方法。
  • 但在 HashMap 的基础上做了扩展,体现了多态性。主要是三种方法:
    • afterNodeRemoval:将被删除的节点从 LinkedHashMap 维护的双向链表中移除。
    • afterNodeInsertion:用来判断是否删除 LinkedHashMap 维护的双向链表的头结点,即最久未被访问的节点。
    • afterNodeAccess:将传入的node节点放置末尾,即最近访问的元素。
java">    //将 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;
    }
	
	//evict:true:删除最久未被访问的元素,即双向链表的头结点
    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);
        }
    }

	//节点e是刚刚访问的节点,判断是否需将其移动至双向链表的尾结点
    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;
        }
    }

五、总结

  • 我们知道 HashMap 并不能保证有序性,而 LinkedHashMap 作为 HashMap 子类解决了排序的问题。在构造时,通过传入afterNodeAccess = true 来设置LinkedHashMap是有序的。
  • 通过维护双向来链表来保证有序性,拥有变量 head 和 tail 分别指向双向链表的头结点和尾结点,越靠近 尾结点,越是最近访问的节点,越是靠近头结点,越是越久未被访问的节点。
  • 可用于实现 LRU 算法:
    • 使用 LinkedHashMap 实现 LRU:

      java">class LRUCache extends LinkedHashMap<Integer, Integer>{
          private int capacity;
          
          public LRUCache(int capacity) {
              super(capacity, 0.75F, true);
              this.capacity = capacity;
          }
      
          public int get(int key) {
              return super.getOrDefault(key, -1);
          }
      
          public void put(int key, int value) {
              super.put(key, value);
          }
      
          @Override
          protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
              return size() > capacity; 
          }
      }
      
    • 不适用 LinkedHashMap 实现 LRU:

      java">class LRUCache {
          
          static class Node{
              public int key;
              public int val;
              public Node prev;
              public Node next;
              public Node(){
                  this.key = -1;
                  this.val = -1;
              }
              public Node(int key,int val){
                  this.key = key;
                  this.val = val;
              }
          }
          //最大容量    
          int capacity;
          //节点数量
          int size;
          //虚拟头节点
          Node dummyHead;
          //虚拟尾节点
          Node dummyTail;
          
          Map<Integer,Node> map = new HashMap<>();
          
          public LRUCache(int capacity) {
              this.capacity = capacity;
              this.size = 0;
              dummyHead = new Node();
              dummyTail = new Node();
              dummyHead.next = dummyTail;
              dummyTail.prev = dummyHead;
          }
          
          public int get(int key) {
              if(!map.containsKey(key)){
                  return -1;
              }
              Node node = map.get(key);
              
              //将该节点从原位置删除
              remove(node);
              //将该节点添加到链表尾部
              addLeast(node);
              
              return node.val;
          }
          
          public void put(int key, int value) {
              Node cur = new Node(key,value);
              remove(map.get(key));
              addLeast(cur);
          }
          
          //删除节点
          public void remove(Node node){
              if(node == null) return;
              node.prev.next = node.next;
              node.next.prev = node.prev;
              node.next = null;
              node.prev = null;
              size --;
              
              map.remove(node.key);
          }
          
          //将节点添加到尾部
          public void addLeast(Node node){
              Node prev = dummyTail.prev;
              prev.next = node;
              node.prev = prev;
              node.next = dummyTail;
              dummyTail.prev = node;
              size ++;
              map.put(node.key,node);
              //超过最大容量了
              if(size > capacity){
                  removeFirst();
              }
          }
          
          //删除头节点
          public Node removeFirst(){
              if(dummyHead.next == dummyTail) return null;
              Node remove = dummyHead.next;
              remove(remove);
              return remove;
          }
      }
      
      

http://www.niftyadmin.cn/n/5476533.html

相关文章

如何恢复被.locked勒索病毒加密的服务器和数据库?

.locked勒索病毒有什么特点&#xff1f; .locked勒索病毒的特点主要包括以下几个方面&#xff1a; 文件加密&#xff1a;.locked勒索病毒会对受感染设备上的所有文件进行加密&#xff0c;包括图片、文档、视频和其他各种类型的重要文件。一旦文件被加密&#xff0c;文件的扩展…

每天学习python30分钟(第三天)

一.面向对象 了解oop for i in range(1,5):for j in range(1,5):for k in range(1,5):if((i!j) and (i!k) and (j!k)):print(i,j,k) 二.类 基础的类使用 class CuteCat: #通过calss创建可爱猫猫类def __init__(self,cat_name,cat_age,cat_color): #定义属性s…

前端学习<二>CSS基础——16-浏览器的兼容性问题

我们在div里放一个img&#xff0c;发现&#xff1a; 在html和html5中&#xff0c;div的长宽是不同的&#xff0c;后者的高度要超过几个像素。 比如说&#xff0c;下面这个是html的。 <!DOCTYPE html><html lang"en"><head><meta charset"…

2024-04-07 作业

作业要求&#xff1a; 1> 思维导图 2> 自由发挥应用场景实现一个登录窗口界面。 【可以是QQ登录界面、也可以是自己发挥的登录界面】 要求&#xff1a;尽量每行代码都有注释 作业1&#xff1a; 作业2&#xff1a; 运行代码&#xff1a; #include "myqwidget.h&quo…

揭秘程序员面试技巧,助你轻松拿offer!

上文 程序员面试是求职者展现自身技术实力、沟通能力和职业素养的关键环节。为了在面试中脱颖而出&#xff0c;求职者需要掌握一些实用的面试技巧。以下将详细阐述程序员面试技巧&#xff0c;助您在面试中取得更好的成绩。 一、面试前准备 了解公司及职位 在面试前&#xff0…

ngnix的反向代理是什么?有什么作用?

1、Nginx的反向代理是什么&#xff1f; Nginx的反向代理是一种网络架构模式&#xff0c;其中Nginx服务器作为前端服务器&#xff0c;接收客户端的请求&#xff0c;然后将这些请求转发给后端服务器&#xff08;例如Java应用程序服务器&#xff09;。在这个过程中&#xff0c;客…

二维相位解包理论算法和软件【全文翻译- 掩码(3.4)】

本节我们将研究从质量图中提取掩码的问题。掩码是一个质量图,其像素只有两个值:0 或 1。零值像素标志着质量最低的相位值,这些相位值将被屏蔽、零权重或忽略。第 5 章中的某些 L/ 正则算法需要使用掩码来定义零权重。掩码还可用于某些路径跟踪算法,如第 4.5 节中将要介绍的…

从传统方案到scrollIntoView:元素锚点与显示区域的优化之旅

在网页开发中&#xff0c;将特定元素精确地定位并显示在用户的可视区域内是一项常见且关键的任务。这通常涉及到元素锚点与显示区域的联动&#xff0c;确保用户无需手动滚动即可直接看到目标内容。本文将首先分析现有的元素锚点到显示区域的方案及其存在的问题&#xff0c;然后…