zoukankan      html  css  js  c++  java
  • LinkedHashMap笔记

    一、最基本元素存储单元

     /**
         * HashMap.Node subclass for normal LinkedHashMap entries.
         */
        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);
            }
        }
     还是原HashMap的,多了两个引用before,after,说明这是要用双向链表

    二、初始化 构造方法

    public LinkedHashMap() {
            super();
            accessOrder = false;
        }
        多了 accessOrder = false;
        
         /**
         * The iteration ordering method for this linked hash map: <tt>true</tt>
         * for access-order, <tt>false</tt> for insertion-order.
         *
         * @serial
         */
          final boolean accessOrder;
        accessOrder表示迭代顺序,true表示访问顺序,false表示插入顺序。   

    三、put方法

    put方法没有重写,因此和HashMap是一样的,但也有不同,不同在于LinkedHashMap实现了afterNodeAccess,afterNodeInsertion方法
    看HashMap put源码

     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                       boolean evict) {
            Node<K,V>[] tab; Node<K,V> p; int n, i;
            if ((tab = table) == null || (n = tab.length) == 0)
                n = (tab = resize()).length;
            if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
            else {
                Node<K,V> e; K k;
                if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                    e = p;
                else if (p instanceof TreeNode)
                    e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                else {
                    for (int binCount = 0; ; ++binCount) {
                        if ((e = p.next) == null) {
                            p.next = newNode(hash, key, value, null);
                            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                                treeifyBin(tab, hash);
                            break;
                        }
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            break;
                        p = e;
                    }
                }
                if (e != null) { // existing mapping for key
                    V oldValue = e.value;
                    if (!onlyIfAbsent || oldValue == null)
                        e.value = value;
                    afterNodeAccess(e);  //这个方法HashMap是空实现,这里是发生hash冲突后,找到有相同key对值进行处理时调用
                    return oldValue;
                }
            }
            ++modCount;
            if (++size > threshold)
                resize();
            afterNodeInsertion(evict);   //这个方法HashMap也是空实现,这里是完成新数据put后调用
            return null;
        }



    LinkedHashMap具体实现 :
    1.void afterNodeAccess(Node<K,V> e) { // move node to last 注释就说明了是把该元素移到最后 LinkedHashMap.Entry<K,V> last; if (accessOrder && (last = tail) != e) { //accessOrder用到了,默认false等于不运行,true时是按插入顺序 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; } } 以上代码看出在按插入顺序时,在有相同key时,对当前节点将其移到链表末尾 2.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); } } removeEldestEntry 方法在jdk1.8中固定返回false,因此不用关注此方法 说明一下removeElestEntry用于定义删除最老元素的规则。一旦需要删除最老节点,那么将会调用removeNode删除节点。
    举个例子,如果一个链表只能维持100个元素,那么当插入了第101个元素时,以如下方式重写removeEldestEntry的话,那么将会删除最老的一个元素


     四、get方法(get进行了重写) ,在插入模式中,获取值后又一次进行把当前节点移到链表尾部操作

      有点类似LRU(最少最近使用)算法~

     /*基于访问排序*/
            LinkedHashMap<String, String> linkedHashMap_accessOrder = new LinkedHashMap<>(10,0.75f,true);
            linkedHashMap_accessOrder.put("name1", "josan1");
            linkedHashMap_accessOrder.put("name2", "josan2");
            linkedHashMap_accessOrder.put("name3", "josan3");
            linkedHashMap_accessOrder.put("name4", "josan4");
            linkedHashMap_accessOrder.put("name5", "josan5");
    
            linkedHashMap_accessOrder.get("name5");
            linkedHashMap_accessOrder.get("name1");
            linkedHashMap_accessOrder.get("name2");
            linkedHashMap_accessOrder.get("name4");
            linkedHashMap_accessOrder.get("name3");
            //51243顺序
            for (Map.Entry<String, String> entry : linkedHashMap_accessOrder.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                System.out.println("key:" + key + ",value:" + value);
            }

    get方法: (accessOrder 为true表示按照访问排序),

      public V get(Object key) {
            Node<K,V> e;
            if ((e = getNode(hash(key), key)) == null)
                return null;
            if (accessOrder)
                afterNodeAccess(e);
            return e.value;
        }

     将节点移动到最后:

     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;
            }
        }

    层层判断和更改引用之后,p已经独立出来了,就把p移到了map的末尾,实现了访问排序

    总结

    1. HashMap中TreeNode 即红黑树的节点类是继承LinkedHashMap.Entry

    2. HashMap中没有使用双向链表,before, after没有使用,单纯的红黑树

    3.LinkedHashMap是HashMap的子类,很多方法都是继承自父类, 

       LinkedHashMap中,存取等使用的也使用红黑树,但维护了before, after的链表属性,在存取时一样使用红黑树算法,

      但keySet()、values()以及entrySet()等迭代时使用的是双向链表来进行的,这样的双向链表结构保证了插入顺序的有序。

     4.总得来说,LinkedHashMap底层是数组加单项链表加双向链表

     5.数组加单向链表就是HashMap的结构,记录数据用,双向链表,存储插入顺序用。然后LInkedHashMap重写了两个方法,一个是添加entry,一个是创建entry,这两种时刻都要维护双向链表。

      

    参考出处:https://www.cnblogs.com/zhaojj/p/7832680.html

  • 相关阅读:
    linux中的 tar命令的 -C 参数,以及其它一些参数
    dockerfile 介绍
    linux安装mysql后root无法登录
    centos搭建本地yum源,
    centos7下载自定义仓库的镜像设置方法
    QT TCP文件上传服务器
    QT UDP聊天小程序
    QT 网络编程三(TCP版)
    QT 网络编程二(UDP版本)
    QT 网络编程一
  • 原文地址:https://www.cnblogs.com/coloz/p/11041697.html
Copyright © 2011-2022 走看看