源码分析

LinkedHashMap源码分析

2018-07-06  本文已影响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机制解决并发问题~

上一篇下一篇

猜你喜欢

热点阅读