LinkedHashMap实现原理浅析

        LinkedHashMap是Hash表和链表的实现,LinkedHashMap继承于HashMap,在使用父类方法的基础上重新实现了用于维护链表的几个方法函数,这几个函数的作用分别是:节点访问后、节点插入后、节点移除后做的一些事情,下面贴出源码。
public class LinkedHashMap<K,V> extends HashMap<K,V>  implements Map<K,V>{

    /**
     * HashMap.Node subclass for normal LinkedHashMap entries.
     * LinkedHashMap的条目继承自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);
        }
    }

    private static final long serialVersionUID = 3801124242820219131L;

    /**
     * 双向链表头节点(最年老的),也就是最先加入链表中的条目
     */
    transient LinkedHashMap.Entry<K,V> head;

    /**
     * 双链表的尾节点(最年轻的),也就是最近加入链表中的条目
     */
    transient LinkedHashMap.Entry<K,V> tail;

    /**
     *链表中元素的排序顺序,true为按访问先后排序,false为按插入先后排序
     * @serial
     */
    final boolean accessOrder;

     // 链表末端的链接
    private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
    	//之前的表尾元素赋值为最后一个节点
        LinkedHashMap.Entry<K,V> last = tail;
        //当前新增加节点作为表尾节点
        tail = p;
        //如果之前没有表尾节点,就相当于是空链表,那么当前元素作为表头节点
        if (last == null)
            head = p;
        //将当前节点的上一节点指向原来最末尾的那个节点,将原来最末尾的那个节点的下一节点指向当前节点,以此建立前后关系
        else {
            p.before = last;
            last.after = p;
        }
    }

    //删除树节点后需要引用替换
    private void transferLinks(LinkedHashMap.Entry<K,V> src,
                               LinkedHashMap.Entry<K,V> dst) {
        LinkedHashMap.Entry<K,V> b = dst.before = src.before;
        LinkedHashMap.Entry<K,V> a = dst.after = src.after;
        if (b == null)
            head = dst;
        else
            b.after = dst;
        if (a == null)
            tail = dst;
        else
            a.before = dst;
    }

    //使用父类HashMap的方法将此链表重置为初始默认状态。
    void reinitialize() {
        super.reinitialize();
        head = tail = null;
    }
    //创建一个新的节点
    Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        LinkedHashMap.Entry<K,V> p =new LinkedHashMap.Entry<K,V>(hash, key, value, e);
        //将节点进行关联
        linkNodeLast(p);
        return p;
    }
    //替换树节点引用
    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;
    }
    //新创建一个树节点
    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;
    }
    //替换树节点的引用
    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;
    }
    //节点删除后,
    void afterNodeRemoval(Node<K,V> e) { 
    	//当前被删除节点
        LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e;
        //当前被删除节点的上一节点
        LinkedHashMap.Entry<K,V> b = p.before;
        //当前被删除节点的下一节点
        LinkedHashMap.Entry<K,V> a = p.after;
        //当前被删除节点的上下节点引用清空
        p.before = p.after = null;
        //如果上一节点不存在,则下一节点作为头节点(被删除节点为head时)
        if (b == null)
            head = a;
        //否则被删除节点的下一节点直接作为被删除节点的上一节点的下一节点
        else
            b.after = a;
        //如果被删除节点的下一节点不存在,则被删除节点的上一节点就成了最后一个节点
        if (a == null)
            tail = b;
        //否则被删除节点的上一节点被作为被删除节点的下一节点的上一节点
        else
            a.before = b;
    }
    //节点插入后,则会根据情况判断是否删除超过最大容量的那些最早放入的节点
    void afterNodeInsertion(boolean evict) {
        LinkedHashMap.Entry<K,V> first;
        //表头不为null,并且removeEldestEntry(first)返回true,也就是设置为访问排序
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
    }
    //这个方法是节点元素访问后 如get,将节点移动到最后位置
    //为了便于理解链表排序维护,我们将按照人事组织结构说明如下实现原理
    void afterNodeAccess(Node<K,V> e) { 
        LinkedHashMap.Entry<K,V> last;
        //如果accessOrder为true,也就是说如果链表是按访问排序并且排除掉了只有当前一个节点的情况
        if (accessOrder && (last = tail) != e) {
        	//我(当前访问的节点)
            LinkedHashMap.Entry<K,V> p =(LinkedHashMap.Entry<K,V>)e;
            //我的直属上司(上一个节点)
            LinkedHashMap.Entry<K,V> b = p.before;
            //我的下属(下一个节点)
            LinkedHashMap.Entry<K,V> a = p.after;
            //将我原来那个下属直接干掉,人手有点多,裁员
            p.after = null;
            //情况一:如果我没有上司,那么我的下属则会成为老板,带头大哥(说明访问我时我在表头位置)
            if (b == null)
                head = a;
            //否则的话,我的下属会直属于我的上司管,也有可能我没有下属,就是a = null,
            //要么就是我的下属就是我自己,那我就还是那个上司管( a = p.after = p)
            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;
            //将职位调动次数加1
            ++modCount;
        }
    }

    void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
        for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
            s.writeObject(e.key);
            s.writeObject(e.value);
        }
    }

    /**
     * 构造函数,默认按插入方式排序
     * @param initialCapacity 初始容量
     * @param loadFactor  加载因子
     */
    public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }

   
    /**
     * 构造函数,默认按插入方式排序
     * @param initialCapacity 初始化容量
     */
    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }

 
    /**
     * 构造函数,默认按插入方式排序
     */
    public LinkedHashMap() {
        super();
        accessOrder = false;
    }

 
    /** 
     * 构造函数,默认按插入方式排序
     * @param m  实现了map接口的参数集合
     */
    public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }


    /**
     * @param initialCapacity  初始化容量
     * @param loadFactor 加载因子
     * @param accessOrder  排序方式  true为访问先后  false为插入先后
     */
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }


   
    /* (non-Javadoc)
     * @see java.util.HashMap#containsValue(java.lang.Object)
     */
    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;
    }

    
    public V get(Object key) {
        Node<K,V> e;
        //根据key调用父类的获取节点方法,如果为null则返回null
        if ((e = getNode(hash(key), key)) == null)
            return null;
        //如果设置来访问排序,那么就需要进行访问后的操作,将此节点移到链表最后
        if (accessOrder)
            afterNodeAccess(e);
        //返回对应的value
        return e.value;
    }

    /* (non-Javadoc)
     * @see java.util.HashMap#getOrDefault(java.lang.Object, java.lang.Object)
     * 如果对应key的值为null,则返回给定默认值
     */
    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;
   }

    /**
     * {@inheritDoc}
     */
    public void clear() {
        super.clear();
        head = tail = null;
    }

   /*
    * 提供给子类实现的扩展方法,如果返回true,则按照访问先后排序
    * */
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
    }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值