我想实现一个缓存,其键和值是软引用,因为它将包含的实例可能需要很长时间才能计算(特别是值),我希望包含未引用对象的条目作为键当且仅当我可能耗尽内存时,或者作为获取垃圾的值。如果我对值使用了弱引用,那么一旦它们被引用就会被垃圾收集,这不是我想要做的。
起初,我使用this example。它可以工作,但每个缓存实例创建一个单独的线程让我很烦恼,它使用强引用键和我自己删除陈旧条目(如WeakHashMap)在缓存类的某些方法调用中(显然)当我冒险运行时当我不打电话给他们时,内存不足。我想使用Guava,但MapMaker不再允许使用软键,这是合乎逻辑的,因为默认等价基于相等(==)而不是equals()方法,这意味着无法重新创建相同的键。但是,我同意sebastien-lorber's comment:
我认为如果Guava重写了SoftReference的equals方法,软键会有意义。我已经看到Guava使用了“等价机制”,我认为,对于软引用,defaultEquivalence不应该是identity而是equals,以处理这种情况。
我也看了MapDB和JCS。
如何修改上面引用的示例或使用Guava基于软引用创建缓存,最好使用equals()而不是==来确定键的相等性?
答案 0 :(得分:1)
我想唯一完美的解决方案是在你提出的番石榴中加CacheBuilder.softKeysDelegatingEquals
。
跳到最后以获得更好的主意
也许这种解决方法可以:
Cache
中添加内容时,请将新SoftReference<Key>
放入您自己的HashSet
。removalListener
添加Cache
,然后从SoftReference
HashSet
无论如何,你不需要线程。
第二部分有点棘手,因为您需要Key
并需要删除其相应的软引用。为此,您可以使用WeakHashMap<Key, SoftReference<Key>
替换该集,或者通过让您的密钥为equals
来引用hacky:
class Key {
Key(Somedata somedata) {
this.somedata = somedata;
}
@Override public int hashCode() {
return somedata.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj instanceof Key) {
return somedata.equals(((Key) obj).somedata);
}
if (obj instanceof KeySoftReference) {
return equals(((KeySoftReference) obj).get());
}
return false;
}
@NonNull private final Somedata somedata;
}
// Hacky, never use for anything else.
class KeySoftReference extends SoftReference<Key> {
protected KeySoftReference(Key key) {
super(key);
this.hashCode = key.hashCode();
}
@Override public int hashCode() {
return hashCode;
}
@Override public boolean equals(Object obj) {
if (this == obj) return true;
if (obj instanceof Key) {
return ((Key) obj).equals(this);
}
if (obj instanceof KeySoftReference) {
Key key = get();
// This makes no two cleared reference equal to each other,
// That's OK as long as you never create two references to the same object.
return key!=null && key.equals(((KeySoftReference) obj).get());
}
return false;
}
private final int hashCode;
}
我想,这符合合同,但正如我所说,它是黑客和未经测试的。
有一个简单的解决方案。使用
CacheBuilder.weakKeys().softValues()
并向SoftReference<Key>
添加Value
。这使得密钥可以轻松到达。
如果你真的需要比较是平等的,那么你可能会运气不好,虽然WeakInterner<Key>
可以提供帮助。
答案 1 :(得分:0)
实际上,我刚刚在Guava中重新引入了软键(在版本14中删除了它),但是在CacheBuilder中: https://code.google.com/p/guava-libraries/issues/detail?id=1845
然后,我必须调用非公共方法CacheBuilder.keyEquivalence(Equivalence.equals()),以便使用equals()而不是==来比较键。我会告诉你它是否有效。在这种情况下,我会接受我自己的答案。否则,我将不得不使用maaartinus的建议。