android中内存缓存是如何实现的

那就有必要来看看LruCache源代码了 里面有一个重要的数据结构LinkedHashMap。具体讲解在这里(http://blog.csdn.net/lxj1137800599/article/details/54974988) 在此总结一下用法: 1.添加一个数据。先找到数组中对应的index,然后把数据放到链表的最后位置。由于是双向链表,那么就等于放在header.prv 2.获取一个数据。先找到数组中对应的index,然后找到数据所在的位置。如果是按照读取顺序来排序的,那么还要将这个节点放到双向链表的最后一位(这个特性,可以实现LRU算法)

public class LruCache<K, V> {
    //map用来存储外界的缓存对象
    private final LinkedHashMap<K, V> map;

    // 构造函数
    public LruCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        //设置accessOrder为true,按照读取顺序来存储缓存
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
    }

    //获取一个缓存对象
    public final V get(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V mapValue;
        synchronized (this) {
            //设置为true了,还要将这个节点放到双向链表的最后一位
            mapValue = map.get(key);
            if (mapValue != null) {
                hitCount++;
                return mapValue;
            }
            missCount++;
        }

        /*
         * Attempt to create a value. This may take a long time, and the map
         * may be different when create() returns. If a conflicting value was
         * added to the map while create() was working, we leave that value in
         * the map and release the created value.
         */

        V createdValue = create(key);
        if (createdValue == null) {
            return null;
        }

        synchronized (this) {
            createCount++;
            //试着添加一个新值
            //如果是要添加数据的,mapValue=null,size扩大然后trimToSize
            //如果是替换数据,mapValue!=null
            mapValue = map.put(key, createdValue);
            if (mapValue != null) {
                map.put(key, mapValue);
            } else {
                size += safeSizeOf(key, createdValue);
            }
        }

        if (mapValue != null) {
            entryRemoved(false, key, createdValue, mapValue);
            return mapValue;
        } else {
            trimToSize(maxSize);
            return createdValue;
        }
    }

    //添加一个缓存对象
    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);
            // previous = null表示新添加的缓存之前未存在过
            // previous != null表示之前已存在数据
            if (previous != null) {
                // 之前已有数据,那么size再减回去
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            entryRemoved(false, key, previous, value);
        }

        trimToSize(maxSize);
        return previous;
    }

    //重点在这里****************************************
    //如果超出最大容量,那就去掉header.next
    //由于新添加的数据已经跑到header.prv去了,所以删除的必定是最近最少使用的
    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 || map.isEmpty()) {
                    break;
                }

                Map.Entry<K, V> toEvict = map.entrySet().iterator().next();
                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                size -= safeSizeOf(key, value);
                evictionCount++;
            }

            entryRemoved(true, key, value, null);
        }
    }
}

总结如下:accessOrder设置为true。 当添加缓存时,先添加数据,再把对应的entry挪到双向链表的末尾。如果size超过最大值,就删除header.next 当获取缓存时,先获取数据。由于设置为true,那么也会将对应的entry挪到双向链表的末尾

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏黒之染开发日记

js中“||”和“&&”的高级用法

没想到会有人收藏,而且这些不是我自己悟出来的,是网络上找到的一篇文章我读懂后转过来的,原文是http://www.jb51.net/article/21339....

44820
来自专栏吴伟祥

java 常规类型消息的格式化 原

0.slf4j有一个common logger没有的功能,字符串中的{}会被替换,如下:

13950
来自专栏小灰灰

Java容器篇小结之Map自问自答

采用问答的方式对常见的问题进行整理小结 I. Map篇 0. 什么是Map 看到这个有点懵逼,一时还真不知道怎么解释,能让完全没有接触过的人都能听懂 想到生活...

208100
来自专栏逸鹏说道

C# 温故而知新:Stream篇(—)

目录: 什么是Stream? 什么是字节序列? Stream的构造函数 Stream的重要属性及方法 Stream的示例 Stream异步读写 Stream ...

35890
来自专栏开发与安全

《linux c 编程一站式学习》课后部分习题解答

1、假设变量x和n是两个正整数,我们知道x/n这个表达式的结果要取Floor,例如x是17,n是4,则结果是4。如果希望结果取Ceiling应该怎么写表达式呢?...

48160
来自专栏james大数据架构

java中用MessageFormat格式化json字符串用占位符时出现的问题can't parse argument number

在MessageFormat.format方法中组装jason数据字符串:{code:"w1",des:"w2"},起止分别有左大括号和右大括号。 直接写的点位...

60080
来自专栏Ldpe2G的个人博客

MXNet 源码解读系列之一 C++端如何解析NDArray参数文件

要想弄清楚MXNet 是如何解析参数文件,并从中提取预训练好的权值,首先第一步要看

1.3K60
来自专栏从流域到海域

《笨办法学Python》 第39课手记

《笨办法学Python》 第39课手记 本节课讲列表的操作,用来做练习的代码中出现了之前用到过的几个函数,是一节复习课。你需要记住它们。 原代码如下: ten_...

21670
来自专栏博岩Java大讲堂

Java集合--ConcurrentMap

56490
来自专栏积累沉淀

【译】Java 8的新特性—终极版

声明:本文翻译自Java 8 Features Tutorial – The ULTIMATE Guide,翻译过程中发现并发编程网已经有同学翻译过了:...

262100

扫码关注云+社区

领取腾讯云代金券