【问题标题】:How to limit the maximum size of a Map by removing oldest entries when limit reached [closed]如何在达到限制时通过删除最旧的条目来限制地图的最大大小[关闭]
【发布时间】:2012-07-13 05:15:37
【问题描述】:

我想要一个具有最大尺寸的 Map 实现。我想将其用作缓存,因此一旦达到限制,就会删除最旧的条目。

我也不想引入对任何 3rd 方库的依赖。

【问题讨论】:

  • 你为什么不想依赖 3rd 方库?我宁愿为这样的事情使用一个设计良好/测试良好的库,而不是花费精力构建和测试我自己的
  • 那么你尝试了什么,你得到了什么错误?这是一个我的家庭作业问题
  • 我正在编写一个小型库,并试图避免强制依赖或添加不必要的批量。
  • 我没有遇到错误,我只是想了解其他人如何解决这个问题。这不是家庭作业。
  • @Kev,我同时发布了问题和我自己的答案。我对我的解决方案很满意,所以想在这里发布它纯粹是为了与其他人分享(在这里没有找到满意的答案之后)。提出了一个更好的解决方案,我学到了一些东西。极好的!我认为要求很简洁。你能解释一下我怎样才能做得更好,或者为什么这对其他人没有用处?

标签: java caching collections map


【解决方案1】:

你可以像这样使用LinkedHashMap

您可以通过 LRU 或 FIFO 移除。

public static <K, V> Map<K, V> createLRUMap(final int maxEntries) {
    return new LinkedHashMap<K, V>(maxEntries*10/7, 0.7f, true) {
        @Override
        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
            return size() > maxEntries;
        }
    };
}

【讨论】:

  • 为什么不使用 Wea​​kHashMap?
  • 使用 Wea​​kHashMap 作为缓存有两个问题。 1)它的大小没有上限 2)它可以一次全部清除,它对GC上清除的数量没有限制。
  • 这很完美,我不知道LinkedHashMap的那个特性。谢谢!
  • @Peter:你为什么让 initialCapacity 比 maxCapacity 大?
  • @WaleryStrauch HashMap 和 LinkedHashMap 在达到负载因子时调整大小。例如假设您将其设置为128,它的大小将翻倍,达到128*0.7f。一种解决方案是使容量maxEntries*10/7 或使用负载因子1.0f
【解决方案2】:

我使用个人课程来满足您的需求。一旦地图已满,插入时最旧的访问对象将被删除。它使用 hashmap 和链表。

你有两种阅读方法:

  • peek(不会将对象标记为最近访问)
  • get(将对象标记为最近访问过,从而暂时阻止其删除)

这是课程(cmets 被删除,因为它们是法语):

import java.util.HashMap;
import java.util.Map;

public final class HashCache<K, T> {

    public interface Disposer<T> {
        void dispose(T t);
    }

    class Maillon {
        public K cle;

        public Maillon precedent;
        public Maillon suivant;
        public T contenu;
    }

    private Map<K, Maillon> barrette = new HashMap<K, Maillon>();

    private Maillon sommet;
    private Maillon fond;
    private int tailleCache = 100;

    private Disposer<T> disposer;

    @SuppressWarnings("unchecked")
    public HashCache(int tailleCache) {
        if (tailleCache>2) this.tailleCache = tailleCache;
        Maillon[] maillons = new HashCache.Maillon[tailleCache];
        sommet = maillons[0] = new Maillon();
        for (int i=1; i<tailleCache; i++) (maillons[i-1].suivant = maillons[i] = new Maillon()).precedent = maillons[i-1];
        fond = maillons[tailleCache-1];
        //check();
    }

    public Object getLastRead() {
        return sommet.contenu;
    }

    public Object peek(K cle) {
        Object m = barrette.get(cle);
        if (m==null) return null;
        return ((Maillon) m).contenu;
    }

    public synchronized void remove(K cle) {
        Maillon m = (Maillon) barrette.remove(cle);
        if (m!=null) {
            if (disposer!=null) disposer.dispose(m.contenu);
            m.contenu = null;
            m.precedent.suivant = m.suivant;
            m.suivant.precedent = m.precedent;
            fond.suivant = m;
            m.precedent = fond;
            fond = m;
        }
    }


    public synchronized Object put(K cle, T valeur) {
        if (cle==null) return valeur;

        if (fond.cle!=null) {
            barrette.remove(fond.cle);
            if (disposer!=null) disposer.dispose(fond.contenu);
        }
        fond.cle = cle;
        barrette.put(cle, fond);
        fond.contenu = valeur;
        fond.suivant = sommet;
        sommet.precedent = fond;
        sommet = fond;
        fond = fond.precedent;
        fond.suivant = null;

        return valeur;
    }

    public int computeDepth(K cle) {
        Maillon m = sommet;
        for (int i=0; i<tailleCache; i++) {
            if (cle.equals(m.cle)) return i;
            m=m.suivant;
            if (m==null) {
                break;
            }
        }
        return -1;
    }

    @SuppressWarnings("unchecked")
    public synchronized void removeAll() {
        if (disposer!=null) {
            for (Maillon m : barrette.values()) {
                disposer.dispose(m.contenu);
            }
        }
        barrette = new HashMap<K, Maillon>();
        Maillon[] t = new HashCache.Maillon[tailleCache];
        sommet = t[0] = new Maillon();
        for (int i=1; i<tailleCache; i++) (t[i-1].suivant = t[i] = new Maillon()).precedent = t[i-1];
        fond = t[tailleCache-1];
    }


    public synchronized T get(K cle) {
        Maillon m = barrette.get(cle);
        if (m == sommet) return m.contenu;
        if (m == null) return null;
        if (m == fond) {
            m.precedent.suivant = null;
            m.suivant = sommet;
            sommet.precedent = m;
            sommet = m;
            fond = m.precedent;
            m.precedent = null;
        } else {
            m.suivant.precedent = m.precedent;
            m.precedent.suivant = m.suivant;
            m.suivant = sommet;
            sommet.precedent = m;
            sommet = m;
        }
        return m.contenu;
    }

    public Disposer<T> getDisposer() {
        return disposer;
    }

    public void setDisposer(Disposer<T> disposer) {
        this.disposer = disposer;
    }

}

【讨论】:

  • 您选择不实现 Map 接口的任何原因?
  • 是的。但一个不好的:大约 15 年前,当我做这门课时,甚至 HashMap 都不存在(我们有 Hashtable)。后来我做了一些更改,其中包括泛型,但实现 Map 也会很好。我只是直到现在才需要它。
【解决方案3】:

如果这不是作业,我强烈建议使用第三方库来避免设计、测试和维护自己的解决方案的痛苦。

例如,参见Guava MapMaker 机制。

【讨论】:

  • 我通常会这样做,但我在编写一个小型库时会极力避免引入依赖项。感谢您提醒我使用 MapMaker,它非常方便,我将在我的大型项目中使用它。
【解决方案4】:

这是一个仅包装普通 HashMap 并将方法调用委托给它的实现。 唯一的区别是地图不能超过最大容量。

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

public class CacheMap<K, V> implements Map<K, V> {

    private final Map<K, V> delegate = new HashMap<K, V>();
    private Queue<K> keyInsertionOrder = new LinkedList<K>();
    private final int maxCapacity;

    public CacheMap(int maxCapacity) {
        if (maxCapacity < 1) {
            throw new IllegalArgumentException(
                    "Capacity must be greater than 0");
        }
        this.maxCapacity = maxCapacity;
    }

    @Override
    public void clear() {
        delegate.clear();
    }

    @Override
    public boolean containsKey(Object key) {
        return delegate.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return delegate.containsValue(value);
    }

    @Override
    public Set<java.util.Map.Entry<K, V>> entrySet() {
        return delegate.entrySet();
    }

    @Override
    public boolean equals(Object o) {
        return delegate.equals(o);
    }

    @Override
    public V get(Object key) {
        return delegate.get(key);
    }

    @Override
    public int hashCode() {
        return delegate.hashCode();
    }

    @Override
    public boolean isEmpty() {
        return delegate.isEmpty();
    }

    @Override
    public Set<K> keySet() {
        return delegate.keySet();
    }

    @Override
    public V put(K key, V value) {
        V previous = delegate.put(key, value);
        keyInsertionOrder.remove(key);
        keyInsertionOrder.add(key);

        if (delegate.size() > maxCapacity) {
            K oldest = keyInsertionOrder.poll();
            delegate.remove(oldest);
        }
        return previous;
    }

    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        for (K key : m.keySet()) {
            put(key, m.get(key));
        }
    }

    @Override
    public V remove(Object key) {
        keyInsertionOrder.remove(key);
        return delegate.remove(key);
    }

    @Override
    public int size() {
        return delegate.size();
    }

    @Override
    public Collection<V> values() {
        return delegate.values();
    }
}

【讨论】:

    猜你喜欢
    • 2012-07-22
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    • 2015-02-06
    • 2010-11-17
    • 2019-12-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多