美文网首页
LinkedHashMap源码解读

LinkedHashMap源码解读

作者: Noblel | 来源:发表于2018-01-01 09:39 被阅读0次

    先看几个构造方法

    /**
     * 双向链表的头部
     */
    transient LinkedHashMap.Entry<K,V> head;
    
    /**
     * 双向链表的尾端
     */
    transient LinkedHashMap.Entry<K,V> tail;
    
    public LinkedHashMap() {
        super();
        //accessOrder为false则表示按插入顺序排序
        //accessOrder为true则表示按访问顺序排序
        accessOrder = false;
    }
    
    public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }
    
    public LinkedHashMap(int initialCapacity,
                         float loadFactor, 
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }
    
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }
    
    /**
     * 重写父类的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;
    }
    
    /**
     * 把新加的节点放在链表的最后面
     */
    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;
        }
    }
    
    /**
     * 继承于HashMap的Entry,添加了上一个和下一个节点由单向链表变成了双向链表
     */
    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);
        }
    }
    
    /**
     * 这个方法是在调用put的时候,hashMap中的putVal()方法中的afterNodeAccess重写的
     * 在put的时候已经有值了会更新值然后调用此方法
     * 把当前节点移动到双向链表的最末端
     */
    void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMap.Entry<K,V> last;
        //accessOrder默认是false的
        //e不在最末尾就修改链表,将e移到链表最末尾的操作
        if (accessOrder && (last = tail) != e) {
            //b是e的前一个节点,a是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;
            //修改计数器自加1
            ++modCount;
        }
    }
    
    /**
     * 插入完成会掉用此方法,传过来的是false
     */ 
    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);
        }
    }
    
    /**
     * 这个方法一直返回的是false,那就是不需要删除末尾节点,
     * 自己写的时候可以重写给定一个条件控制LinkedHashMap中最老数据在何时删除
     */
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
    }
    
    /**
     * 虽然没有重写put方法,但是重写了get方法
     */
    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;
    }
    

    这个有啥用啊?写了那么多。。作为小白我以前自己开发的时候从来没想过那么多,看到大家都在讨论图片缓存,所以看了一下网上的代码。做出了一些思考,我们显示图片是可以先从内存中获取,然后内存中没有就去本地缓存文件找,本地也没有就去网络获取。这样可以减少服务器的压力,不要每次都访问服务器。

    使用方法

    private LruCache<String, Bitmap> mMemoryCache;  
    
    public ImageDownLoader(Context context){  
        //获取系统分配给每个应用程序的最大内存,不同的厂商手机给的最大值不一样
        int maxMemory = (int) Runtime.getRuntime().maxMemory();    
        int mCacheSize = maxMemory / 8;  
        //给LruCache分配1/8
        mMemoryCache = new LruCache<String, Bitmap>(mCacheSize){  
    
            /**
             * 必须重写此方法,来测量Bitmap的大小  
             */
            @Override  
            protected int sizeOf(String key, Bitmap value) {  
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    return value.getAllocationByteCount();
                }
                return value.getHeight() * value.getRowBytes();
            }  
            
            /**
             * 当缓存被移除时调用,可不重写
             * @param evicted 缓存移除的原因,true表示内存不够移除了,false表示put移除或者remove移除了
             * @param key key
             * @param oldValue
             * @param newValue
             */
            @Override
            protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
                super.entryRemoved(evicted, key, oldValue, newValue);
            }
    
            /**
             * 获取不到的时候调用,可不重写
             * @param key
             * @return
             */
            @Override
            protected Bitmap create(String key) {
                return super.create(key);
            }  
        };  
    }
    
    //添加到缓存
    mMemoryCache.put(key, bitmap);
    
    //获取缓存
    mMemoryCache.get(key);
    

    看一下LruCache

    public LruCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        //直接new了一个LinkedHashMap,把accessOrder设为true,也就是按访问顺序排序
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
    }
    
    public final V put(K key, V value) {
        if (key == null || value == null) {
            throw new NullPointerException("key == null || value == null");
        }
    
        V previous;
        synchronized (this) {
            putCount++;
            size += safeSizeOf(key, value);
            //把以前的存放的值返回过来
            previous = map.put(key, value);
            //以前的值不为空就把刚才加的内存大小恢复
            if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }
    
        //有旧值就执行entryRemoved方法,这是一个空实现,可以自己重写
        if (previous != null) {
            entryRemoved(false, key, previous, value);
        }
    
        trimToSize(maxSize);
        return previous;
    }
    
    /**
     * 看到这我们就知道为什么需要重写了
     */
    protected int sizeOf(K key, V value) {
        return 1;
    }
    
    public void trimToSize(int maxSize) {
        //特定条件跳出循环
        while (true) {
            K key;
            V value;
            synchronized (this) {
                if (size < 0 || (map.isEmpty() && size != 0)) {
                    throw new IllegalStateException(getClass().getName()
                            + ".sizeOf() is reporting inconsistent results!");
                }
    
                if (size <= maxSize) {
                    break;
                }
    
                //当内存超过了最大内存就一直循环
                Map.Entry<K, V> toEvict = map.eldest();
                if (toEvict == null) {
                    break;
                }
    
                key = toEvict.getKey();
                value = toEvict.getValue();
                //移除最不常用的节点
                map.remove(key);
                size -= safeSizeOf(key, value);
                //回收次数自加
                evictionCount++;
            }
            //调用entryRemoved方法,这里true就是说被迫移除
            entryRemoved(true, key, value, null);
        }
    }
    
    public final V get(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }
    
        V mapValue;
        synchronized (this) {
            //从LinkedHashMap中获取,获取方式还是hashMap的查找方式,只是内部维护一个链表做一些删除元素的操作。
            mapValue = map.get(key);
            if (mapValue != null) {
                //命中数自加
                hitCount++;
                return mapValue;
            }
            //miss数自加
            missCount++;
        }
        
        //这个create方法可重写,也就是LinkedHashMap中没有的时候会调用到这里
        V createdValue = create(key);
        if (createdValue == null) {
            return null;
        }
        
        synchronized (this) {
            //创建次数自加
            createCount++;
            //插入到表中
            mapValue = map.put(key, createdValue);
    
            if (mapValue != null) {
                // There was a conflict so undo that last put
                //发生冲突,取消最后一次添加
                map.put(key, mapValue);
            } else {
                size += safeSizeOf(key, createdValue);
            }
        }
    
        if (mapValue != null) {
            //调用entryRemoved,这里false表示内存
            entryRemoved(false, key, createdValue, mapValue);
            return mapValue;
        } else {
            trimToSize(maxSize);
            return createdValue;
        }
    }
    

    最后自己画一个图总结了一下,有错误请指正。共同进步学习。

    原理图

    相关文章

      网友评论

          本文标题:LinkedHashMap源码解读

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