要求如下
该cache有一个固定大小size,如果元素超过size,则需要做元素剔除动作。
剔除的优先级为,有设置优先级的元素需要按照最早过期顺序进行剔除,如果不存在设置优先级的元素,则从总元素中通过LRU剔除一个。
对元素的任何修改动作都需要重置元素的过期时间,例如一个元素已经设置了过期时间,对其进行不带过期时间的add,则会将其过期时间抹除,会变成是一个不带过期时间的元素,除非重新执行待过期时间的add动作。
只要求实现add和get方法。
public static class LRUWithTimeOut {
private int size = Integer.MAX_VALUE;
private LinkedHashMap<String, String> map;
private ConcurrentHashMap<String, TimeoutInfo> timeoutMap = new ConcurrentHashMap<>();
private PriorityQueue<String> queue = new PriorityQueue<>(new ItemWithTimeout(timeoutMap));
public LRUWithTimeOut(int maxSize) {
if (maxSize > 0) {
this.size = maxSize;
}
map = new LinkedHashMap<String, String>(size) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
return this.size() > size;
}
};
}
/**
* @param key
* @param value
*/
public synchronized void add(String key, String value) {
add(key, value, null);
}
public synchronized void add(String key, String value, Long expireTime) {
if (StringUtils.isEmpty(key)) {
throw new RuntimeException("key is empty");
}
if (expireTime != null && expireTime < 0) {
throw new RuntimeException("expireTime can not be negative");
}
queue.remove(key);
timeoutMap.remove(key);
map.remove(key);
if (map.size() == size) {
String item = queue.poll();
if (item != null) {
timeoutMap.remove(item);
map.remove(item);
}
}
map.put(key, value);
if (expireTime != null && expireTime > 0) {
timeoutMap.put(key, new TimeoutInfo(System.currentTimeMillis(), expireTime));
queue.add(key);
}
}
public synchronized String get(String key) {
TimeoutInfo timeoutInfo = timeoutMap.get(key);
if (!map.containsKey(key)) {
return null;
}
if (timeoutInfo == null) {
return map.get(key);
} else {
if (timeoutInfo.getTimeoutMills() + timeoutInfo.getCurrentTimeMills() >= System.currentTimeMillis()) {
queue.remove(key);
timeoutMap.remove(key);
map.remove(key);
return null;
}
queue.remove(key);
timeoutMap.remove(key);
timeoutInfo.setCurrentTimeMills(System.currentTimeMillis());
timeoutMap.put(key, timeoutInfo);
queue.add(key);
return map.get(key);
}
}
@AllArgsConstructor
private class ItemWithTimeout implements Comparator<String> {
private Map<String, TimeoutInfo> timeoutMap = new HashMap<>();
@Override
public int compare(String o1, String o2) {
return timeoutMap.get(o1).expiredTimeMills() - timeoutMap.get(o2).expiredTimeMills() > 0 ? 1 : -1;
}
}
@Data
private class TimeoutInfo {
private long currentTimeMills;
private long timeoutMills;
public TimeoutInfo(long currentTimeMills, long timeoutMills) {
this.currentTimeMills = currentTimeMills;
this.timeoutMills = timeoutMills;
}
private TimeoutInfo() {
}
public long expiredTimeMills() {
return this.currentTimeMills + this.timeoutMills;
}
}
}