Android之LRU算法

LRU算法简介

LRU 全称是 least recently used,意为“最近最少使用”,说白了就是一种淘汰算法,当有新的元素插入进来的时候,我们的使用空间又有限的时候,就需要淘汰旧的元素,这时候就会选择淘汰最近最少使用的元素。

 

应用场景

在app开发中,假设有一个页面列表需要从网络获取大量图片,在页面切换、滑动中,同一张图片不可能每次都去网络获取,这样对内存、性能和用户体验都是一个考验,最好的做法是设置本地文件缓存和内存缓存,存储从网络取得的数据。
但本地文件缓存空间并非是无限大,容量越大读取效率越低,可设置一个折中缓存容量比如10M,如果缓存已满,我们需要采用合适的替换策略换掉一个已有的数据对象,并替之已一个新的数据对象;内存缓存作为最先被读取的数据,应该存储那些经常使用的数据对象,且内存容量有限,内存缓存的容量也应该限定。

 

实现原理

Android中有一个LRU算法实现的集合LruCache。

public class LruCache<K, V> {
    private final LinkedHashMap<K, V> map;
}

可以看出LruCache内部是使用LinkedHashMap存储数据的。其实LruCache主要依靠LinkedHashMap本身的LRU实现来实现的。LruCache只是进行了另一次封装。

    public LruCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
//     这里指定了该集合的最大容量,一旦集合容量大于该容量则会调用trimToSize方法来减少容量。
        this.maxSize = maxSize;
//      这里创建了LinkedHashMap并且第三个参数指定为true.该参数为true时LinkedHashMap开启LRU算法。
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
    }

在使用LruCache时要重写sizeOf方法,来指定成员的实际容量。否则默认返回1

protected int sizeOf(K key, V value) {
        return 1;
    }

LruCache的一个回调函数本身没有实现,可重新。在新增或移除变量时会被调用。第一个参数为true时是移除变量,false时是新增变量。

protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}

接下来简单说下LruCache的存放方法。

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

        if (previous != null) {
//         新增时会调用的回调函数,本身没有具体实现需要使用时要自己重写
            entryRemoved(false, key, previous, value);
        }
//      这里是重点,当插入结束后会检查内存是否超标
        trimToSize(maxSize);
        return previous;
    }




/**
*简单来说就是会无限循环的来检测内存容量
*如果内存容量大于maxSize,则会移除最近最久使用的成员。
*然后继续循环,直到内存容量小于maxSize或者集合为null循环终止
**/
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, key, value, null);
        }
    }



public final V get(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }
//     如果值存在则直接返回值,这里会依赖LinkedHashMap的LRU机制。
        V mapValue;
        synchronized (this) {
            mapValue = map.get(key);
            if (mapValue != null) {
                hitCount++;
                return mapValue;
            }
            missCount++;
        }

//      这里会调用create方法,这个方法本身默认返回null,即如果用户不重写get方法会返回null。
//      根据实际需求来编写。如果创建了新的对象则会存放进集合中。
        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, key, createdValue, mapValue);
            return mapValue;
        } else {
            trimToSize(maxSize);
            return createdValue;
        }
    }

 

资料源自互联网,经综合整理而成,如有侵犯,请联系我。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FIFO算法LRU算法都是为了解决缓存淘汰问题而设计的。FIFO算法是一种按照元素插入顺序排序的数据结构,元素在数据结构中按照先进先出的顺序进行存储和访问。在实现缓存时,可以根据实际情况选择使用FIFO算法LRU算法,以达到最佳的缓存淘汰效果。 LRU(Least Recently Used)算法是一种常见的缓存淘汰算法,它的基本思想是“最近最少使用”。在缓存中,最近使用的数据会被优先淘汰,而最近未使用的数据则需要被淘汰以腾出缓存空间。为了实现这一功能,LRU算法通过定期淘汰最近最少使用的数据来保持缓存的最佳状态。LRU算法的核心思想是使用一个缓存数组,数组中的每个元素都存储了一个键值对。每个键值对表示缓存中的一个数据项,包含数据项的键和值。 FIFO算法LRU算法的区别在于,FIFO算法是按照元素插入顺序排序的,而LRU算法是按照元素最近使用的时间排序的。因此,当缓存中的数据项被频繁访问时,LRU算法的效果会更好,因为它会优先保留最近使用的数据项,而FIFO算法则无法做到这一点。 以下是一个使用Python实现LRU算法的例子: ```python from collections import OrderedDict class LRUCache: def __init__(self, capacity: int): self.cache = OrderedDict() self.capacity = capacity def get(self, key: int) -> int: if key not in self.cache: return -1 self.cache.move_to_end(key) return self.cache[key] def put(self, key: int, value: int) -> None: if key in self.cache: self.cache.move_to_end(key) self.cache[key] = value if len(self.cache) > self.capacity: self.cache.popitem(last=False) ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值