Java 实现简单的内存对象LRU缓存
2016-12-24 23:13
656 查看
常遇到需要将对象在内存中缓存的场景.比如下面场景:
Android 即使通讯应用中,用户列表,对话页面,群聊页面等,都会有大量的用户信息展示需求,至少需要 名字,头像 等信息,需要从服务器获取.
而上述页面有高概率会再次访问,浏览的用户信息也有高概率再次曝光.
每次曝光都请求一次服务器显然太浪费,但将全部用户信息都缓存下来肯定也不合适,因此需要缓存用户信息.
于是自己做了一个简单的基于 LRU 规则的内存缓存容器.
LRU 即 Last Recent Used, 大意是最近被使用的对象最后被丢弃.
先上源码再扯其他:
源码:
当然存入 HashMap 时, value 被套了一层,以便记录最后访问时间.
缓存的构造接受两个参数: 最大对象数, 和清理对象时的对象数的阈值.
然后对象就可以不断存入. 每次有存入对象操作时,就会检查一次 HashMap 中的对象数是否超过了最大值.
如果超过最大值,则开始清理对象,直到对象数达到阈值.
这里用了两个参数来限定对象数目,是基于效率考虑:
如果只有一个对象最大数限制,那么在存入对象到达该最大数后,几乎每次存入都需要到缓存里找到那个最老的对象并删除之.
所以这里设置了一个阈值,比如最大对象数为100,阈值为75.那么在每次对象超过100时,就启动删除流程,删除到对象数为75.这样在接下来的25个对象存入动作中,都不会再触发删除操作,效率明显提高.
比如 LRU 算法一般不止会考虑更新时间那么简单,还会考虑被使用次数等.
比如这个cache只适用于对象数较少时,比如要存一万个对象的话,清理缓存的工作应该放在线程中去做.
清理操作放在线程中的话,同步也是问题.这样就要实现分步清理.
后期还需改进.
Android 即使通讯应用中,用户列表,对话页面,群聊页面等,都会有大量的用户信息展示需求,至少需要 名字,头像 等信息,需要从服务器获取.
而上述页面有高概率会再次访问,浏览的用户信息也有高概率再次曝光.
每次曝光都请求一次服务器显然太浪费,但将全部用户信息都缓存下来肯定也不合适,因此需要缓存用户信息.
于是自己做了一个简单的基于 LRU 规则的内存缓存容器.
LRU 即 Last Recent Used, 大意是最近被使用的对象最后被丢弃.
先上源码再扯其他:
源码:
package lx.af.utils.cache; import android.support.annotation.NonNull; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; /** * author: lx * date: 16-6-1 * * simple memory object LRU cache. based on HashMap. * usage of the cache is simple: * specify a max object count and a purge threshold, then the cache is ready to go. * or you can specify the max count, and the purge threshold will be set to max * 0.75. * * when to purge the cache: * when add objects to the cache, total object count will be checked against max count. * if exceeded, objects will be deleted due to access time order until count reaches threshold. * access time of objects will be recorded on put() and get(), and items with shorter * access time will get purged first. * the purge operation will be done automatically. * * this class is threadsafe. * * @param <K> the type of keys maintained by cache. rule is the same as of HashMap. * @param <V> the type of mapped values. rule is the same as of HashMap. */ public class SimpleMemLruCache<K, V> { private HashMap<K, Wrapper<K, V>> mMap; private int mMax; private int mThreshold; private final Object mLock = new Object(); /** * create cache with max object count. * the threshold will be set to max * 0.75. * @param max max object count of the cache, exceed which will trigger object purge. */ public SimpleMemLruCache(int max) { this(max, (int) (max * 0.75f)); } /** * create cache with both max object count and object purge threshold. * threshold should not be greater then max, or else exception will be thrown. * @param max max object count of the cache, exceed which will trigger object purge. * @param threshold purge threshold: when object exceeds max count, object deletion * will be triggered. oldest object will get deleted first, * until object count reaches threshold. */ public SimpleMemLruCache(int max, int threshold) { if (threshold > max) { throw new IllegalArgumentException("threshold should not be greater than max"); } mMax = max; mThreshold = threshold; mMap = new HashMap<>(max + 3); } /** * put object to cache. * when called, the object's last access time will be set to current time. * @param key key, as in {@link HashMap#put(Object, Object)} * @param value value, as in {@link HashMap#put(Object, Object)} */ public void put(K key, V value) { synchronized (mLock) { mMap.put(key, new Wrapper<>(key, value)); } checkPrune(); } /** * get object from cache. * when called, the object's last access time will be updated to current time. * @param key key, as in {@link HashMap#get(Object)} * @return value, as in {@link HashMap#get(Object)} */ public V get(K key) { synchronized (mLock) { Wrapper<K, V> wrapper = mMap.get(key); if (wrapper != null) { wrapper.update(); return wrapper.obj; } } return null; } /** * clear all cached objects. */ public void clear() { synchronized (mLock) { mMap.clear(); } } // check and purge objects private void checkPrune() { if (mMap.size() < mMax) { return; } synchronized (mLock) { // use list to sort the map first LinkedList<Wrapper<K, V>> list = new LinkedList<>(); list.addAll(mMap.values()); Collections.sort(list); // delete oldest objects for (int i = list.size() - 1; i >= mThreshold; i --) { Wrapper<K, V> wrapper = list.get(i); mMap.remove(wrapper.key); } list.clear(); } } // wrapper class to record object's last access time. private static class Wrapper<K, V> implements Comparable<Wrapper> { K key; V obj; long updateTime; Wrapper(K key, V obj) { this.key = key; this.obj = obj; this.updateTime = System.currentTimeMillis(); } void update() { updateTime = System.currentTimeMillis(); } @Override public int compareTo(@NonNull Wrapper another) { return (int) (updateTime - another.updateTime); } } }
思路:
主要思路是,将 HashMap 作为内存容器, object 以键值对的形式存入这个 HashMap.当然存入 HashMap 时, value 被套了一层,以便记录最后访问时间.
缓存的构造接受两个参数: 最大对象数, 和清理对象时的对象数的阈值.
然后对象就可以不断存入. 每次有存入对象操作时,就会检查一次 HashMap 中的对象数是否超过了最大值.
如果超过最大值,则开始清理对象,直到对象数达到阈值.
这里用了两个参数来限定对象数目,是基于效率考虑:
如果只有一个对象最大数限制,那么在存入对象到达该最大数后,几乎每次存入都需要到缓存里找到那个最老的对象并删除之.
所以这里设置了一个阈值,比如最大对象数为100,阈值为75.那么在每次对象超过100时,就启动删除流程,删除到对象数为75.这样在接下来的25个对象存入动作中,都不会再触发删除操作,效率明显提高.
示例:
基于开篇提到的使用场景,大体使用方法如下:// 创建缓存,最大对象数为100,阈值为75 SimpleMemLruCache<String, UserInfo> mUserCache = new SimpleMemLruCache<>(100, 75); // 获取用户信息时,首先查看缓存内是否存在,不存在则请求服务器 String userId = "some_user_id"; UserInfo user = mUserCache.get(userId); if (user == null) { user = UserInfoRequest.get(); // 将请求到的用户信息放入缓存 mUserCache.put(userId, user); } displayUserInfo(user);
改进:
这个缓存实现是个超级简单的缓存实现,适用场景有限,有很多待改进的地方.比如 LRU 算法一般不止会考虑更新时间那么简单,还会考虑被使用次数等.
比如这个cache只适用于对象数较少时,比如要存一万个对象的话,清理缓存的工作应该放在线程中去做.
清理操作放在线程中的话,同步也是问题.这样就要实现分步清理.
后期还需改进.
相关文章推荐
- Java实现简单的LRU缓存(A Simple LRU Cache in 5 lines)
- 简单的java缓存实现(LRU,LFU,FIFO)
- cache4j轻量级java内存缓存框架,实现FIFO、LRU、TwoQueues缓存模型
- Java实现简单的LRU缓存(A Simple LRU Cache in 5 lines)
- LRU缓存-java简单实现
- Java实现简单的LRU缓存(A Simple LRU Cache in 5 lines)
- Json树形结构数据转Java对象并存储到数据库的实现-超简单的JSON复杂数据处理 .
- Java实现LRU(最近最少使用)缓存
- 用JAVA实现一个简单地Socket服务器,可以实现发给指定对象
- Java中读写锁的实现及使用读写锁简单实现缓存系统的实例
- java实现简单的缓存机制
- 利用LinkedHashMap简单实现基于LRU策略的缓存
- HASHMAP缓存简单java实现
- LRU缓存介绍与实现 (Java)
- LRU缓存介绍与实现 (Java)
- 如何通过mat从java内存dump中找到缓存对象中的所有字符串
- java缓存的简单实现
- Java实现LRU(最近最少使用)缓存
- java中读写锁的实现及使用读写锁简单实现缓存系统的实例
- Lru缓存的简单实现