1.自己定义
package com.aiggo.common.util;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
*
* @Description: 带有效期map 简单实现 实现了基本的方法
* @author: qd-ankang
* @date: 2016-11-24 下午4:08:46
* @param <K>
* @param <V>
*/
public class ExpiryMap<K, V> extends HashMap<K, V>{
private static final long serialVersionUID = 1L;
/**
* default expiry time 2m
*/
private long EXPIRY = 1000 * 60 * 2;
private HashMap<K, Long> expiryMap = new HashMap<>();
public ExpiryMap(){
super();
}
public ExpiryMap(long defaultExpiryTime){
this(1 << 4, defaultExpiryTime);
}
public ExpiryMap(int initialCapacity, long defaultExpiryTime){
super(initialCapacity);
this.EXPIRY = defaultExpiryTime;
}
public V put(K key, V value) {
expiryMap.put(key, System.currentTimeMillis() + EXPIRY);
return super.put(key, value);
}
public boolean containsKey(Object key) {
return !checkExpiry(key, true) && super.containsKey(key);
}
/**
* @param key
* @param value
* @param expiryTime 键值对有效期 毫秒
* @return
*/
public V put(K key, V value, long expiryTime) {
expiryMap.put(key, System.currentTimeMillis() + expiryTime);
return super.put(key, value);
}
public int size() {
return entrySet().size();
}
public boolean isEmpty() {
return entrySet().size() == 0;
}
public boolean containsValue(Object value) {
if (value == null) return Boolean.FALSE;
Set<java.util.Map.Entry<K, V>> set = super.entrySet();
Iterator<java.util.Map.Entry<K, V>> iterator = set.iterator();
while (iterator.hasNext()) {
java.util.Map.Entry<K, V> entry = iterator.next();
if(value.equals(entry.getValue())){
if(checkExpiry(entry.getKey(), false)) {
iterator.remove();
return Boolean.FALSE;
}else return Boolean.TRUE;
}
}
return Boolean.FALSE;
}
public Collection<V> values() {
Collection<V> values = super.values();
if(values == null || values.size() < 1) return values;
Iterator<V> iterator = values.iterator();
while (iterator.hasNext()) {
V next = iterator.next();
if(!containsValue(next)) iterator.remove();
}
return values;
}
public V get(Object key) {
if (key == null)
return null;
if(checkExpiry(key, true))
return null;
return super.get(key);
}
/**
*
* @Description: 是否过期
* @param key
* @return null:不存在或key为null -1:过期 存在且没过期返回value 因为过期的不是实时删除,所以稍微有点作用
*/
public Object isInvalid(Object key) {
if (key == null)
return null;
if(!expiryMap.containsKey(key)){
return null;
}
long expiryTime = expiryMap.get(key);
boolean flag = System.currentTimeMillis() > expiryTime;
if(flag){
super.remove(key);
expiryMap.remove(key);
return -1;
}
return super.get(key);
}
public void putAll(Map<? extends K, ? extends V> m) {
for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
expiryMap.put(e.getKey(), System.currentTimeMillis() + EXPIRY);
super.putAll(m);
}
public Set<Map.Entry<K,V>> entrySet() {
Set<java.util.Map.Entry<K, V>> set = super.entrySet();
Iterator<java.util.Map.Entry<K, V>> iterator = set.iterator();
while (iterator.hasNext()) {
java.util.Map.Entry<K, V> entry = iterator.next();
if(checkExpiry(entry.getKey(), false)) iterator.remove();
}
return set;
}
/**
*
* @Description: 是否过期
* @author: qd-ankang
* @date: 2016-11-24 下午4:05:02
* @param expiryTime true 过期
* @param isRemoveSuper true super删除
* @return
*/
private boolean checkExpiry(Object key, boolean isRemoveSuper){
if(!expiryMap.containsKey(key)){
return Boolean.FALSE;
}
long expiryTime = expiryMap.get(key);
boolean flag = System.currentTimeMillis() > expiryTime;
if(flag){
if(isRemoveSuper)
super.remove(key);
expiryMap.remove(key);
}
return flag;
}
public static void main(String[] args) throws InterruptedException {
ExpiryMap<String, String> map = new ExpiryMap<>(10);
map.put("test", "ankang");
map.put("test1", "ankang");
map.put("test2", "ankang", 3000);
System.out.println("test1" + map.get("test"));
Thread.sleep(1000);
System.out.println("isInvalid:" + map.isInvalid("test"));
System.out.println("size:" + map.size());
System.out.println("size:" + ((HashMap<String, String>)map).size());
for (Map.Entry<String, String> m : map.entrySet()) {
System.out.println("isInvalid:" + map.isInvalid(m.getKey()));
map.containsKey(m.getKey());
System.out.println("key:" + m.getKey() + " value:" + m.getValue());
}
System.out.println("test1" + map.get("test"));
}
}
ref:http://blog.csdn.net/u011534095/article/details/54091337
2.Java自带的LRU cache
package cn.lzrabbit.structure.lru;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Created by liuzhao on 14-5-15.
*/
public class LRUCache2<K, V> extends LinkedHashMap<K, V> {
private final int MAX_CACHE_SIZE;
public LRUCache2(int cacheSize) {
super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
MAX_CACHE_SIZE = cacheSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_CACHE_SIZE;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Map.Entry<K, V> entry : entrySet()) {
sb.append(String.format("%s:%s ", entry.getKey(), entry.getValue()));
}
return sb.toString();
}
}
ref:http://www.cnblogs.com/lzrabbit/p/3734850.html
3.Google guava jar,也是最好用的,如下:
CacheBuilder作为LoadingCache 与 Cache实例的创建者,具有以下特征:
1、自动载入键值至缓存;
2、当缓存器溢出时,采用最近最少使用原则进行替换。
3、过期规则可基于最后读写时间。
4、设置键值引用级别。
5、元素移出通知。
6、缓存访问统计。
示例:
LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
.maximumSize(10)
.expireAfterWrite(10, TimeUnit.SECONDS)
.build(
new CacheLoader<String, Integer>() {
public Integer load(String key) throws Exception {
return loadKey(key);
}
});
例子中设定了缓存大小为10,写后10秒过期,命中失败时会通过load方法将查询键值加入缓存。
缓存的移出策略主要有以下几种:
1)基于缓存权重
LoadingCache<String, Integer> loadingCache = CacheBuilder.newBuilder()
.maximumWeight(10)
.weigher(new Weigher<String, Integer>() {
public int weigh(String k, Integer v) {
return v; //v的权重设为其本身;
}
})
.recordStats()
.build(
new CacheLoader<String, Integer>() {
public Integer load(String key) {
return num++; //num初始值为1;
}
});
System.out.println(loadingCache.get("a"));
System.out.println(loadingCache.get("b"));
System.out.println(loadingCache.get("c"));
System.out.println(loadingCache.get("d"));
System.out.println(loadingCache.get("a"));
输出结果为1 2 3 4 1,当设置为maximumWeight(9)时,输出结果即为1 2 3 4 5,因为在get("d")时,权重值和已超过最大值9,a被移出,get("a")时,需重新加载,此时num为5。
2)基于时间
expireAfterAccess(long, TimeUnit):最后一次访问后的一段时间移出;
expireAfterWrite(long, TimeUnit) :最后一次写入后的一段时间移出;
3 ) 基于引用
键、值默认都是强引用,但键可设置弱引用(weakKeys),值可设置弱(weakValues)或软引用(softValues) 。
强引用:如果一个对象具有强引用,垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
软引用:如果一个对象只具有软引用,如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
弱引用:在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
ref:
通过recordStats()函数,还可开启缓存的访问统计,通过调用status()方法,返回包含统计信息的CacheStats对象,可以获取缓存的很多统计信息:hitCount(命中成功次数),missCount(命中失败次数),loadSuccessCount(载入成功次数),loadExceptionCount(载入失败次数),totalLoadTime(总载入时间),evictionCount(移除次数),requestCount() (访问次数),hitRate()(命中成功率),missRate()(命中失败率),loadCount()(载入次数)等。
ref:http://blog.csdn.net/desilting/article/details/11768773