项目中有要用到类似于Redis失效的工具类,数据量不大,不涉及多线程。用redis太重了。
下面这位仁兄的*不错,开始用了一段时间后,觉得不是很理想,于是自己也造了一个。
项目地址见下方链接。文章中的代码后续可能会在github继续更新,不保证同步性。
有问题的老哥欢迎留言讨论。
链接:
项目开源地址(Github):https://github.com/Heiffeng/expiry-map
项目开源地址(Gitee):https://gitee.com/heika/expiry-map
仁兄的*:https://blog.csdn.net/u011534095/article/details/54091337
代码如下:
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class ExpiryMap<K,V> implements Map<K,V> {
DelayQueue<Ele<K>> queue;
HashMap<K,V> map;
public ExpiryMap() {
queue = new DelayQueue<>();
map = new HashMap<>();
}
@Override
public int size() {
removeExpire();
return map.size();
}
@Override
public boolean isEmpty() {
removeExpire();
return map.isEmpty();
}
@Override
public boolean containsKey(Object key) {
removeExpire();
return map.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
removeExpire();
return map.containsValue(value);
}
@Override
public V get(Object key) {
removeExpire();
return map.get(key);
}
@Override
public V put(K key, V value) {
return map.put(key,value);
}
public V put(K key,V value,long expire,TimeUnit unit){
Ele<K> ele = new Ele<>(key, System.currentTimeMillis() + unit.toMillis(expire));
if(queue.contains(ele)){
queue.remove(ele);
}
queue.add(ele);
return map.put(key,value);
}
@Override
public V remove(Object key) {
Ele<K> eleKey = queue.stream().filter(ele -> ele.getKey().equals(key)).findFirst().orElse(null);
if(eleKey!=null) queue.remove(eleKey);
return map.remove(key);
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
map.putAll(m);
}
@Override
public void clear() {
queue.clear();
map.clear();
}
@Override
public Set<K> keySet() {
removeExpire();
return map.keySet();
}
@Override
public Collection<V> values() {
removeExpire();
return map.values();
}
@Override
public Set<Entry<K, V>> entrySet() {
removeExpire();
return map.entrySet();
}
private void removeExpire(){
Ele ele = null;
while((ele = queue.poll()) != null){
map.remove(ele.getKey());
}
}
private class Ele<K> implements Delayed {
private K key;
private long expireTime;
public Ele(K key, long expireTime) {
this.key = key;
this.expireTime = expireTime;
}
public K getKey() {
return key;
}
@Override
public long getDelay(TimeUnit unit) {
return unit.convert(expireTime - System.currentTimeMillis(),TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
if(o == null) return 1;
long otherDelay = o.getDelay(TimeUnit.MILLISECONDS);
long thisDelay = this.getDelay(TimeUnit.MILLISECONDS);
if(otherDelay > thisDelay){
return -1;
}else if(otherDelay < thisDelay){
return 1;
}else{
return 0;
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Ele<?> ele = (Ele<?>) o;
return key.equals(ele.key);
}
@Override
public int hashCode() {
return Objects.hash(key);
}
}
}
使用示例
简单测试
public static void main(String[] args) throws InterruptedException {
ExpiryMap<String,String> map = new ExpiryMap<>();
map.put("haha","嘻嘻",3, TimeUnit.SECONDS);
Thread.sleep(2000);
System.out.println(map.get("haha")); // 这次可以拿到值
Thread.sleep(2000);
System.out.println(map.get("haha")); // 2+2=4,大于3秒了,所以获取为null
}
重复值插入测试
public static void main(String[] args) throws InterruptedException {
ExpiryMap<String,String> map = new ExpiryMap<>();
map.put("haha","嘻嘻",3, TimeUnit.SECONDS); // 第一次失效时间为3秒
map.put("haha","嘿嘿",5, TimeUnit.SECONDS); // 第二次失效时间为5秒
Thread.sleep(3100);
System.out.println(map.get("haha")); // 3秒后去获取值,能获取到。
Thread.sleep(2000);
System.out.println(map.get("haha")); // 5秒后再次获取,为null
}