具有弱值的HashMap

时间:2012-11-16 08:55:19

标签: java android hashmap weak-references

我正在为持久存储的对象实现缓存。这个想法是:

  • 方法getObjectFromPersistence(long id); ///Takes about 3 seconds
  • 方法getObjectFromCache(long id) //Instantly

并使用以下伪代码:getObject(long id)

synchronized(this){
    CustomObject result= getObjectFromCache(id)
    if (result==null){
       result=getObjectFromPersistence(id);
       addToCache(result);
    }
    return result;
}

但我需要允许垃圾收集器收集CustomObject。到目前为止,我使用HashMap<Long,WeakReference<CustomObject>进行实施。问题是随着时间的推移,HashMap被填充为空WeakReferences

我已经检查了WeakHashMap,但是密钥很弱(并且值仍然是强引用),因此使用WeakReferences的长信是没有意义的。

什么是解决此问题的最佳解决方案?是否有一些“逆WeakHashMap”或类似的东西?

由于

8 个答案:

答案 0 :(得分:26)

您可以使用Guava MapMaker

ConcurrentMap<Long, CustomObject> graphs = new MapMaker()
   .weakValues()
   .makeMap();

您甚至可以通过将makeMap()替换为:

来包含计算部分
   .makeComputingMap(
       new Function<Long, CustomObject>() {
         public CustomObject apply(Long id) {
           return getObjectFromPersistence(id);
         }
       });

由于您所写的内容看起来很像缓存,因此更新,更专业的Cache(通过CacheBuilder构建)可能与您更相关。它不直接实现Map接口,但提供了更多可能需要缓存的控件。

您可以参考this了解如何使用CacheBuilder的详细信息,以下是快速访问的示例:

LoadingCache<Integer, String> cache = CacheBuilder.newBuilder()
   .maximumSize(100)
   .expireAfterWrite(10, TimeUnit.MINUTES)
   .build(
       new CacheLoader<Integer, String>() {
           @Override
           public String load(Integer id) throws Exception {
               return "value";
           }
       }
   ); 

答案 1 :(得分:5)

在收集参考资料的施工时间,WeakReference会添加poll

每当您访问缓存时,您都可以ReferenceQueue HashMap<WeakReference<CustomObject>,Long>,并保留{{1}}以了解在队列中找到引用时要删除的条目。

或者,如果不经常使用缓存,则可以在单独的线程中ReferenceQueue

答案 2 :(得分:4)

您是否尝试过android.util.LruCache(它是一个SDK11类,但它在兼容性包中也是android.support.v4.util.LruCache)。它没有实现java.util.Map,但是像Map一样工作,你可以定义它需要多少内存,它将自己刷新旧的(未使用的缓存对象)。

答案 3 :(得分:2)

你可以开始“清理” - 每隔一段时间线程一次。也许如果您的地图大小超过阈值但最多每5分钟......就像那样。

保持清理周期短,不会阻止主要功能。

答案 4 :(得分:2)

答案 5 :(得分:2)

我认为最好的选择(如果对Guava的依赖是不可取的)将使用WeakReference的自定义子类来记住它的ID,以便你的清理线程可以在清除WeakReferences期间删除弱值。

弱引用的实现,必要的ReferenceQueue和清理线程看起来像这样:

class CustomObjectAccess {

    private static final ReferenceQueue<CustomObject> releasedCustomObjects = 
                                                                  new ReferenceQueue<>();

    static {
        Thread cleanupThread = new Thread("CustomObject cleanup thread")                  
            while (true) {
                CustomObjectWeakReference freed = (CustomObjectWeakReference) 
                                CustomObjectWeakReference.releasedCustomObjects.remove();
                cache.remove(freed.id);
            }
        };
        cleanupThread.start();
    }

    private Map<CustomObjectID, CustomObjectWeakReference> cache;

    public CustomObject get(CustomObjectID id) {
        synchronized(this){
            CustomObject result= getFromCache(id);
            if (result==null) {
                result=getObjectFromPersistence(id);
                addToCache(result);
            }
        }
        return result;
    }

    private addToCache(CustomObject co) {
        cache.put(CustomObject.getID(), new CustomObjectWeakReference(co));
    }

    private getFromCache(CustomObjectID id) {
        WeakReference<CustomObject> weak = cache.get(id);
        if (weak != null) {
            return weak.get();
        }
        return null;
    }

    class CustomObjectWeakReference extends WeakReference<CustomObject> {

        private final CustomObjectID id;

        CustomObjectWeakReference(CustomObject co) {
            super(co, releasedCustomObjects);
            this.id = co.getID();
        }
    }
}

答案 6 :(得分:0)

我需要存储标记的弱对象并弄清楚,而不是使用WeakHashMap<String, T>,我可以只使用WeakHashMap<T, String>

这是Kotlin,但应同样适用于Java:

abstract class InstanceFactory<T> {
    @Volatile
    private var instances: MutableMap<T, String> = WeakHashMap<T, String>()

    protected fun getOrCreate(tag: String = SINGLETON, creator: () -> T): T =
        findByTag(tag)?.let {
            it
        } ?: synchronized(this) {
            findByTag(tag)?.let {
                it
            } ?: run {
                creator().also {
                    instances[it] = tag
                }
            }
        }

    private fun findByTag(tag: String): T? = instances.entries.find { it.value == tag }?.key

    companion object {
        const val SINGLETON = "singleton"
    }
}

可以如下使用:

class Thing(private val dependency: Dep) { ... }

class ThingFactory(private val dependency: Dep) : InstanceFactory<Thing>() {

    createInstance(tag: String): Thing = getOrCreate(tag) { Thing(dependency) }

}

简单的单例可以这样完成:

object ThingFactory {
    getInstance(dependency: Dependency): Thing = getOrCreate { Thing(dependency) }
}

答案 7 :(得分:0)

ReferenceMap中有Apache Commons Collections,这是一个硬键软值的map实现(与WeakHashMap相反)。