Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data structure that supports efficient kick-out policy based on recently accessed items

I need a data structure that would support the most efficient kick-out policy for items that are were requested the longest time ago. e.g I have bunch of items that are being requested time to time. when I run out of memory I want to kick out the oldest items in my data structure (hash map).

I was thinking about FIFO ds smth like Queue, but I am not sure how to deal with this situation:

--> a b c d a -->

a is both oldest and newest item. If on adding "a" again I need to search entire queue to delete it. It is very inefficient, but I cannot think of any efficient way of doing it. Maybe some kind of a tree structure that keeps the least accessed one at the top?

Are there any implementation of such data structure in Java already?

like image 993
YohanRoth Avatar asked Mar 05 '23 06:03

YohanRoth


2 Answers

LinkedHashMap is the structure you're after. From the docs:

A special constructor is provided to create a linked hash map whose order of iteration is the order in which its entries were last accessed, from least-recently accessed to most-recently (access-order). This kind of map is well-suited to building LRU caches.

So you should just use the appropriate constructor:

Map<K, V> map = new LinkedHashMap<>(CAPACITY, LOAD_FACTOR, true);

The boolean argument determines if the map is access-order or insertion-order. true means access-order.

Furthermore, if you want your map to work as a LRU cache, you can create your own class that extends LinkedHashMap and overrides the removeEldestEntry method. Again, from the docs:

The removeEldestEntry(Map.Entry) method may be overridden to impose a policy for removing stale mappings automatically when new mappings are added to the map.

This means that you could create your own class for the cache:

public class Cache<K, V> extends LinkedHashMap<K, V> {

    private static final int MAX_ENTRIES = 100;

    public Cache() {
        super(SOME_INITIAL_CAPACITY, SOME_LOAD_FACTOR, true);
    }

    protected boolean removeEldestEntry(Entry<K, V> entry) {
        return size() > MAX_ENTRIES;
    }
}

Usage:

Map<K, V> map = new Cache<>();
like image 68
fps Avatar answered Mar 09 '23 00:03

fps


EDIT: LinkedHashMap has built-in functionality for this, see the other answer.

You could wrap a LinkedHashSet (or LinkedHashMap, depending on your use case) and re-insert items on access, moving them to the end. If you run out of memory, start deleting at the front.

class LruMap<K, V> {
  LinkedHashMap<K, V> map = new LinkedHashMap<>()

  V get(K key) {
    V result = map.remove(key);
    map.put(key, result);
    return result;
  }

  void put(K key, V value) {
    map.put(key, value);
  }

  void removeEldest() {
    if (map.size() > 0) {
      map.remove(map.keySet().iterator().next());
    }
  }
}
like image 31
Stefan Haustein Avatar answered Mar 08 '23 22:03

Stefan Haustein