首页 > 编程语言 >LinkedHashMap&LinkedHashSet源码解析

LinkedHashMap&LinkedHashSet源码解析

时间:2024-08-24 09:49:05浏览次数:10  
标签:node Node LinkedHashSet int value 源码 key capacity LinkedHashMap

LinkedHashMap

概述

LinkedHashSet使用适配器模式包装了LinkedHashSet

一个有序的散列表,允许key为null也允许value为空,从名字上可以看出使用链表维护了有序性

在元素存储时,在原来的HashMap的数组+链表的基础上,为每个元素添加了pre和next指针,构成了一个双向链表

注意:内部没有使用红黑树

同样的内部有多个参数

  • head:虚拟头结点

  • tail:虚拟尾节点

  • 初始容量:16

  • 负载因子:扩容临界值

插入

  • 插入到对应的bucket的单链表中

  • 插入到双向链表的尾部

删除

  • 删除对应bucket中的元素

  • 双向链表中断链

缓存用法

LinkedHashMap有一个子类方法protected boolean removeEldestEntry(Map.Entry<K,V> eldest),该方法的作用是告诉Map是否要删除“最老”的Entry,所谓最老就是当前Map中最早插入的Entry,如果该方法返回true,最老的那个元素就会被删除。

重写这个子方法可以实现一个LRU缓存

LRU缓存

使用LinkedHashMap实现一个指定容量的LRU缓存

public 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类,使用构造方法指定容量、负载因子、顺序规则(true访问顺序,false插入顺序),然后重写removeEldestEntry方法即可,在超过指定大小时返回true

如何手写一个LinkedHashMap?

手写LinkedHashMap一般指手动实现一个LRU缓存

其中包含四个部分

  • 自定义Node节点:需要使用双向链表

  • 自定义一个内部map:需要使用散列表

  • 初始化capacity、size、head、tail,其中头尾是虚拟节点

  • 实现四个方法

    • moveToHead:移动到头部

    • addToHead:添加到头部

    • removeEldestNode:删除尾巴节点

    • removeNode:删除节点---断链

插入节点

创建Node,添加进map中,添加到链表头部,超容量就删除尾部节点,删除的时候调用removeNode断链,然后在map中也删除

查询节点

查询map中你的Node,移动到头部(先断链,再添加)

实现代码

public class MyLRUCache {

    static class Node {
        int key;
        int value;
        Node pre;
        Node next;
        public Node(){}
        public Node(int key, int value) {
            this.key = key;
            this.value = value;
            this.pre = null;
            this.next = null;
        }
    }

    private Map<Integer, Node> cache = new HashMap<>();
    private int size;
    private int capacity;
    private Node head, tail;

    public MyLRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        head = new Node();
        tail = new Node();
        head.next = tail;
        tail.pre = head;
    }

    private void moveToHead(Node node){
        //移动到头部
        removeNode(node);
        addToHead(node);
    }

    private void addToHead(Node node){
        //添加到头部
        node.next = head.next;
        node.pre = head;
        head.next.pre = node;
        head.next = node;
    }

    private void removeEldestNode(){
        //删除尾巴节点
        Node temp = tail.pre;
        removeNode(temp);
        this.cache.remove(temp.key);
    }

    private void removeNode(Node node) {
        //删除节点
        node.pre.next = node.next;
        node.next.pre = node.pre;
    }


    public int get(int key) {
        Node node = this.cache.get(key);
        if(node == null) return -1;
        //移动到头部
        moveToHead(node);
        return node.value;
    }

    public void put(int key, int value) {
        Node node = this.cache.get(key);
        if(node == null) {
            //不存在就创建,添加进哈希表
            node = new Node(key, value);
            cache.put(key, node);
            //添加进头部
            addToHead(node);
            size++;
            if(size > capacity){
                //删除尾巴节点
                removeEldestNode();
                size--;
            }
        }else {
            node.value = value;
            moveToHead(node);
        }
    }
}

总结

使用HashMap作为存储容器,容器中不是存储元素自身,而是存储Node节点,并且Node节点互相连接形成了一个双向链表

标签:node,Node,LinkedHashSet,int,value,源码,key,capacity,LinkedHashMap
From: https://www.cnblogs.com/lilizzyy/p/18377417

相关文章