美文网首页源码分析
LinkedHashMap源码分析

LinkedHashMap源码分析

作者: 丹青水 | 来源:发表于2018-07-06 17:19 被阅读0次

    1 集合特性

    对于集合框架关注点:

    1. 集合底层实现的数据结构是什么 数组+链表+红黑树
    2. 集合中元素是否允许为空 是
    3. 是否允许重复的数据 否
    4. 是否有序(这里的有序是指读取数据和存放数据的顺序是否一致) 是
    5. 是否线程安全。 否

    2 LinkedHashMap分析

    LinkedHashMap 继承HashMap
    没有重写put resize等方法 因此基本数据结构是相同的数组、链表、红黑树

    public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>
        
        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);
        }
    }
    

    那么为啥能保持有序呢?先初始化一个LinkedHashMap

        public LinkedHashMap() {
            super();//和hashMap一样
            accessOrder = false;//表示顺序类型,true查询,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;
    

    分析下put方法
    put方法没有重写,因此和HashMap是一样的 重写了newNode

        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;
        }
        //这里可以对比下hashMap,少了 linkNodeLast(p)
        
        //这个地方维护一个插入的头尾关系,本质可以结合LinkedList的实现很相似
        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;
            }
        }
    

    除此之外LinkedHashMap实现了afterNodeAccess,afterNodeInsertion方法,这个都依赖accessOrder这个参数,如果是false则表示原来的结构不会改变,如果为true则会改变以前的结构

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

    3 LinkedHashMap和LRU缓存

    LRU:最近最少使用算法

    来看个例子:

            
            LinkedHashMap linkedHashMap=new LinkedHashMap();
            linkedHashMap.put(1, 1);
            linkedHashMap.put(2, 2);
            linkedHashMap.put(3, 3);
            linkedHashMap.put(4, 4);
            linkedHashMap.put(3, 5);
            linkedHashMap.get(4);
            System.out.println(linkedHashMap.toString());
            //输出结果为{1=1, 2=2, 3=5, 4=4}没啥问题
            
    

    我们改变accessOrder的查询方式

            LinkedHashMap linkedHashMap1= new LinkedHashMap(16, 0.75f, true);
            linkedHashMap1.put(1,1);
            linkedHashMap1.put(2,2);
            linkedHashMap1.put(3, 3);
            linkedHashMap1.put(4, 4);
            System.out.println(linkedHashMap1.get(2));
            System.out.println(linkedHashMap1.get(1));
            System.out.println(linkedHashMap1.toString());
            //运行结果{3=3, 4=4, 2=2, 1=1}
    

    那么我们可以根据LinkedHashMap这个特性写一个简单的LRU缓存

    public class LruMap<K,V> extends LinkedHashMap<K,V>{
        private int maxSize;
        public LruMap(int maxSize) {
            super(16,0.75f,true);
            this.maxSize = maxSize;
        }
        @Override
        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
            return size()>maxSize;
        }
        public static  void main(String[] args){
            LruMap<Integer,Integer> lruMap=new LruMap<>(3);
            lruMap.put(1,1);
            lruMap.put(2,2);
            lruMap.put(3,3);
            lruMap.put(4,4);
            System.out.println(lruMap.toString());
            //运行结果{2=2, 3=3, 4=4} 发现4已经把3替换掉了
        }
    }
    
    

    ps:我们知道LinkedHashMap是非线程安全的,所以LruMap这个缓存在多线程环境下是有问题的,我们可以重写起增删改的方法,加上synchronized关键字或者使用ReentrantLock机制解决并发问题~

    相关文章

      网友评论

        本文标题:LinkedHashMap源码分析

        本文链接:https://www.haomeiwen.com/subject/wqebuftx.html