寻找 java.util.Map 的直接替代品

2022-09-04 21:15:20

问题

这个问题之后,似乎基于文件或磁盘的实现可能是我在那里提到的问题的正确解决方案。简短版本:Map

  • 现在,我有一个实现为.MapConcurrentHashMap
  • 条目以相当固定的速率不断添加到其中。稍后将对此进行详细介绍。
  • 最终,无论如何,这意味着JVM的堆空间不足。

在工作中,有人(强烈)建议我使用SQLite解决这个问题,但是在问了前面的问题之后,我不认为数据库是这项工作的正确工具。所以 - 让我知道这是否听起来很疯狂 - 我认为更好的解决方案是存储在磁盘上。Map

坏主意:自己实现这个。更好的主意:使用别人的图书馆!哪一个?

要求

必备品:

  • 自由。
  • 持续。数据需要在 JVM 重新启动之间保持不变。
  • 某种可搜索性。是的,我需要能够检索这些的数据并将其丢弃。基本结果集筛选是一个加号。
  • 独立于平台。需要在 Windows 或 Linux 计算机上可进行生产部署。
  • 可清除。磁盘空间是有限的,就像堆空间一样。我需要摆脱几天前的条目。如果我必须手动执行此操作,这没什么大不了的。n

可有可无:

  • 易于使用。如果我能在本周末之前完成这项工作,那就太好了。
    更好的是:一天的结束。如果我能在我的类路径中添加一个JAR,进行更改
    并完成,那将是非常非常棒的。new ConcurrentHashMap<Foo, Bar>();new SomeDiskStoredMap<Foo, Bar>();
  • 不错的可扩展性和性能。最坏的情况是:新条目(平均)每秒添加3次,每秒,全天,每天。但是,插入并不总是那么顺利。可能是那时。(no inserts for an hour)(insert 10,000 objects at once)

可能的解决方案

Ehcache和Berkeley DB现在看起来都很合理。在这两个方向上有什么特别的建议吗?


答案 1

更新(首次发帖后约4年...):请注意,在较新版本的ehcache中,缓存项目的持久性仅在付费产品中可用。感谢@boday指出这一点。

ehcache很棒。它将为您提供在内存,磁盘或内存中实现映射所需的灵活性,并溢出到磁盘。如果你把这个非常简单的包装器用于java.util.Map,那么使用它非常简单:

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

import org.apache.log4j.Logger;

import com.google.common.collect.Sets;

public class EhCacheMapAdapter<K,V> implements Map<K,V> {
    @SuppressWarnings("unused")
    private final static Logger logger = Logger
            .getLogger(EhCacheMapAdapter.class);

    public Cache ehCache;

    public EhCacheMapAdapter(Cache ehCache) {
        super();
        this.ehCache = ehCache;
    } // end constructor

    @Override
    public void clear() {
        ehCache.removeAll();
    } // end method

    @Override
    public boolean containsKey(Object key) {
        return ehCache.isKeyInCache(key);
    } // end method

    @Override
    public boolean containsValue(Object value) {
        return ehCache.isValueInCache(value);
    } // end method

    @Override
    public Set<Entry<K, V>> entrySet() {
        throw new UnsupportedOperationException();
    } // end method

    @SuppressWarnings("unchecked")
    @Override
    public V get(Object key) {
        if( key == null ) return null;
        Element element = ehCache.get(key);
        if( element == null ) return null;
        return (V)element.getObjectValue();
    } // end method

    @Override
    public boolean isEmpty() {
        return ehCache.getSize() == 0;
    } // end method

    @SuppressWarnings("unchecked")
    @Override
    public Set<K> keySet() {
        List<K> l = ehCache.getKeys();
        return Sets.newHashSet(l);
    } // end method

    @SuppressWarnings("unchecked")
    @Override
    public V put(K key, V value) {
        Object o = this.get(key);
        if( o != null ) return (V)o;
        Element e = new Element(key,value);
        ehCache.put(e);
        return null;
    } // end method


    @Override
    public V remove(Object key) {
        V retObj = null;
        if( this.containsKey(key) ) {
            retObj = this.get(key);
        } // end if
        ehCache.remove(key);
        return retObj;
    } // end method

    @Override
    public int size() {
        return ehCache.getSize();
    } // end method

    @Override
    public Collection<V> values() {
        throw new UnsupportedOperationException();
    } // end method

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

答案 2

你从来没有听说过流行率框架吗?

编辑有关该术语的一些说明。

正如 James Gosling 现在所说,没有一个 SQL 数据库比内存中存储更有效。流行框架(最着名的是prepaylerspace4j)是建立在内存中,也许可以存储在磁盘上的存储的想法之上的。它们是如何工作的?实际上,它看似简单:存储对象包含所有持久性实体。此存储只能通过可序列化操作进行更改。因此,将对象放入存储是在隔离上下文中执行的 Put 操作。由于此操作是可序列化的,因此它也可能(取决于配置)保存在磁盘上以进行长期持久性。然而,主要的数据存储库是内存,它以高内存使用率为代价,无疑加快了访问时间。

另一个优点是,由于它们明显简单,这些框架几乎不包含超过十分之一的类。

考虑到你的问题,Space4J的使用立即浮现在我的脑海中(因为它为很少使用的对象的“钝化”提供了支持,也就是说它们的索引键在内存中,但是只要不使用它们,这些对象就会保留在磁盘上)。

请注意,您也可以在c2wiki上找到一些信息。