从HashMap中删除最旧的对象以达到一定大小?大型健康警告

2022-09-03 03:40:24

我在Java中有一个哈希图,我需要限制大小(50000的数量级)。但是我应该只删除最旧的项目。项目的时间戳存储在输入对象的字段中:

Map<String, MyModel> snapshot = new  HashMap<>();

public class MyModel { 
    private ZonedDateTime createdAt;
    // other fields...
}

我还按该时间戳的顺序将它们插入到地图中。

完成这种最旧条目删除的最有效方法是什么?请注意,时间上的“阈值”是未知的,只有所需的地图的最终大小。


答案 1

HashMap没有“最老”,没有“第一”,没有秩序

另一方面,A正是为此而设计的,它在条目之间维护一个双链表,因此请保持它们按插入顺序排列,它还提供了一个 removeEldestEntry 方法:LinkedHashMap

public static void main(final String args[]) throws Exception {
    final int maxSize = 4;
    final LinkedHashMap<String, String> cache = new LinkedHashMap<String, String>() {
        @Override
        protected boolean removeEldestEntry(final Map.Entry eldest) {
            return size() > maxSize;
        }
    };

    cache.put("A", "A");
    System.out.println(cache);
    cache.put("B", "A");
    System.out.println(cache);
    cache.put("C", "A");
    System.out.println(cache);
    cache.put("D", "A");
    System.out.println(cache);
    cache.put("E", "A");
    System.out.println(cache);
    cache.put("F", "A");
    System.out.println(cache);
    cache.put("G", "A");
}

输出:

{A=A}
{A=A, B=A}
{A=A, B=A, C=A}
{A=A, B=A, C=A, D=A}
{B=A, C=A, D=A, E=A}
{C=A, D=A, E=A, F=A}

大型健康警告

请注意,此实现不是 。如果多个线程同时访问链接的哈希映射,并且至少有一个线程在结构上修改了映射,则它必须在外部进行。这通常是通过自然封装映射的某些对象来实现的。如果不存在此类对象,则应使用该方法“包装”映射。这最好在创建时完成,以防止意外地对地图进行不同步访问:synchronizedsynchronizedsynchronizingCollections.synchronizedMap

Map m = Collections.synchronizedMap(new LinkedHashMap(...));

LinkedHashMapJavaDoc


答案 2

最简单的方法是在将某些内容放入映射时将 String 对象添加到列表中。然后你可以做:

while(map.size()>50000){
    map.remove(list.get(0))
    list.remove(0);
}

这之所以有效,是因为您实际上并不关心时间,只关心顺序。

在这方面,队列比列表更好,因为除了访问和删除第一个元素之外,您不需要其他任何东西