问题描述
最近(在一次采访中)我被要求设计 HashMap
,其 TTL 与每个键相关联。我使用下面给出的类似方法完成了它,但在他看来,这不是一个好方法,因为这需要在整个地图上进行迭代,如果地图大小以百万为单位,那么这将是一个瓶颈。
有没有更好的方法来做同样的事情?此外,他只担心一个线程会一直在后台运行,尽管下一个 TTL 是几个小时之后。
class CleanerThread extends Thread {
@Override
public void run() {
System.out.println("Initiating Cleaner Thread..");
while (true) {
cleanMap();
try {
Thread.sleep(expiryInMillis / 2);
} catch (InterruptedException e) {
e.printstacktrace();
}
}
}
private void cleanMap() {
long currentTime = new Date().getTime();
for (K key : timeMap.keySet()) {
if (currentTime > (timeMap.get(key) + expiryInMillis)) {
V value = remove(key);
timeMap.remove(key);
System.out.println("Removing : " + sdf.format(new Date()) + " : " + key + " : " + value);
}
}
}
}
解决方法
最好使用 LinkedHashMap
以便您可以保留插入顺序。实际上 LinkedHashMap
从 HashMap
扩展。如果运行线程是问题所在,那么您可以通过扩展 LinkedHashMap
来创建地图的自定义实现。在类中,覆盖 get
方法。
EDIT :基于 onkar 的评论。最好覆盖 get
而不是 put
,因为这会阻止检索过期项目。
public class MyLinkedHashMap<K> extends LinkedHashMap<K,Date> {
private static final long expiryTime = 100000L;
private long currentOldest = 0L;
@Override
public Date get(Object key) {
long currentTime = new Date().getTime();
if ((currentOldest > 0L) && (currentOldest + expiryTime) < currentTime) {
// even the oldest key has not expired.
return super.get(key);
}
Iterator<Map.Entry<K,Date>> iter = this.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<K,Date> entry = iter.next();
long entryTime = entry.getValue().getTime();
if (currentTime >= entryTime + expiryTime) {
iter.remove();
} else {
// since this is a linked hash map,order is preserved.
// All the elements after the current entry came later.
// So no need to check the remaining elements if the current is not expired.
currentOldest = entryTime;
break;
}
}
return super.get(key);
}
}
,
当你谈论 TTL 并且你想按照它们的 TTL 值的顺序访问它们时,你应该使用 PriorityQueue 或 PriorityBlockingQueue 或 HeapMaps(如果 Java 有一个 HeapMap 实现则是 idk)。
每当您插入一个项目时,它都会在 Collection 中随机排列到正确的排序位置。
因此,如果您只想取出已过期的 TTL,您只需检查/获取,您将首先获得最早过期的,然后继续检查/获取,直到您遇到 TTL 尚未过期的第一个。那就是你停下来的地方。 因为 PriorityQueues 保证(如果你正确地执行了 compareTo 函数)所有的元素总是被排序,所以在点击第一个未过期的条目后,a)该条目将最接近到期,b)所有其他元素将有一个稍后到期。队列中的最后一项 - 与您放置它们的顺序无关 - 将是最晚到期的一项。