首页 > 解决方案 > Java Map with TimeToLive 与每个键/值对相关联

问题描述

最近我被要求(在一次采访中)设计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);
            }
        }
    }
}

标签: javahashmapttl

解决方案


最好使用 aLinkedHashMap以便您可以保留插入顺序。事实上LinkedHashMapHashMap. 如果运行线程是问题所在,那么您可以通过扩展LinkedHashMap. 在类内部,重写get方法。

编辑:基于 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);
    }
}

推荐阅读