【问题标题】:Keep cache(map) small by finding last used element efficiently通过有效地查找最后使用的元素来保持缓存(地图)小
【发布时间】:2016-03-28 01:20:17
【问题描述】:

我有一个 Java 应用程序,它为大平铺图像的重新采样区域提供服务。

由于连续的区域查询通常彼此靠近,因此将图像切片缓存在哈希图中是有意义的。现在我想阻止这个缓存无限增长。

为了不损失性能,我需要一个 O(1)/O(logN) 方法来查找最长时间未访问的地图元素。有没有办法做到这一点,它与仅从缓存中删除随机元素相比如何?

堆或 bst 将允许我保持上次访问列表的排序,但在其中一个中更新最后一次访问需要线性时间。

这是我目前使用的代码的摘录:

Map<Point, BufferedImage> loadedImages = new ConcurrentHashMap<>();
Deque<Point> lastUsed = new ConcurrentLinkedDeque<>();

int getRGB(double tileX, double tileY) {
    Point point = new Point((int) tileX, (int) tileY);
    if (!loadedImages.containsKey(point)) {
        loadedImages.put(point, ImageIO.read(new File("R:\\tiles\\22\\" + point.y + "_" + point.x + ".jpg")));
        lastUsed.addLast(point);
    }
    BufferedImage img = loadedImages.get(point);
    if (loadedImages.size() > 1000) {
        loadedImages.remove(lastUsed.pollFirst());
    }
    //do stuff with img
}

这不是最佳选择,因为加载时间最长的图像可能在一秒钟前才被访问。

【问题讨论】:

  • 您使用什么语言?例如,Java 在其 Collections 类中附带了一些选项。
  • 是的,Java。你在想什么?
  • 确切标准更新您的问题,了解何时应删除陈旧元素。请记住,您甚至可能还没有处理此问题的代码/状态,在这种情况下,您应该添加它,然后更新您的问题。
  • 看看here.
  • LinkedHashMap 似乎完全符合我的要求,doesn't seem to be a synchronized implementation though

标签: algorithm caching data-structures


【解决方案1】:

LinkedHashMap 和Collections.synchronizedMap(linkedHashMap) 一起成功了。 LinkedHashMap 的 removeEldestEntry 方法允许定义何时删除最后访问的条目的条件。

final int MAX_BUF_SIZE = 1000;

Map<Point, BufferedImage> loadedImages = new LinkedHashMap(MAX_BUF_SIZE + 1, .75F, true) {

    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return size() > MAX_BUF_SIZE;
    }
};

int getRGB(double tileX, double tileY) {
    Point point = new Point((int) tileX, (int) tileY);
    if (!loadedImages.containsKey(point)) {
        loadedImages.put(point, ImageIO.read(new File("R:\\tiles\\22\\" + point.y + "_" + point.x + ".jpg")));
    }

    BufferedImage img = loadedImages.get(point);
    //do stuff with img..
}

【讨论】:

    【解决方案2】:

    使用 LinkedHashMap 作为 LRUCache - 简单示例:

    public class LRULinkedHashMap extends LinkedHashMap<Integer, String> {
    
        /**
         * 
         */
        private static final long serialVersionUID = 1L;
        private int capacity;
    
        public LRULinkedHashMap(int capacity) {
            // initialise the capacity, and when to 'double' the capacity (75% of capacity) and 
            // 'true' if the ordering should be done based on the last
            // access (from least-recently accessed to most-recently accessed) 
            super(capacity, 0.75f, true);
            this.capacity = capacity;
        }
    
    
        /**
         * Returns true if this map should remove its eldest entry. 
         * This method is invoked by put and putAll after inserting a new entry into the map. 
         * It provides the implementor with the opportunity to remove the eldest entry each 
         * time a new one is added. This is useful if the map represents a cache: it allows 
         * the map to reduce memory consumption by deleting stale entries.
         */
        @Override
        protected boolean removeEldestEntry(Entry<Integer, String> eldest) {
            // this will return true and remove the eldest entry every time the size of the map
            // is bigger than the capacity - the size() will never go beyond double the capacity
            // (it will double automatically when it hits 75% of original capacity) as this ensures
            // any 'put' entry over the capacity is removes the eldest object 
            return size() > this.capacity;
        }
    
    
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
            // Last Recently Used LinkedHashMap Cache
            LRULinkedHashMap cache = new LRULinkedHashMap(6);
    
            // put some objects into the map
            cache.put(1, "one");
            System.out.println(cache);
            cache.put(2, "two");
            System.out.println(cache);
            cache.put(3, "three");
            System.out.println(cache);
            cache.put(4, "four");
            System.out.println(cache);
            cache.put(5, "five");
            System.out.println(cache);
            cache.put(6, "six"); // capacity (6) reached
            System.out.println(cache + "  <-- Capacity Reached");
            cache.put(7, "seven"); // 1 is removed - eldest
            System.out.println(cache + "  <-- 1 removed");
            cache.put(8, "eight"); // 2 is removed - next eldest
            // access an object before it is removed
            System.out.println(cache + "  <-- 2 Removed");
            cache.get(4); // 4 retrieved placed after 8 - nothing removed (we've only changed the order, not put anything into the map)
            System.out.println(cache + "  <-- '4' Retrieved, access order changed only");
            cache.put(9, "nine"); // 3 is removed - next eldest (4 was retrieved!) 
            System.out.println(cache + "  <-- 3 Removed");
            cache.put(10, "ten"); // 5 removed - next eldest
            // first item is always the eldest - will be next to go if item retrieved or put in map
            System.out.println(cache + "  <-- 5 Removed");
    
        }
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-11
      • 2015-10-02
      • 2017-06-29
      • 2019-11-15
      • 1970-01-01
      相关资源
      最近更新 更多