我想实现一个缓存,其键和值是软引用,因为它将包含的实例可能要花费很长的时间才能计算出来(尤其是值),并且我希望包含未引用对象的项作为键或值当且仅当我可能内存不足时才能收集垃圾。如果我使用弱引用作为值,则一旦不引用它们就会被垃圾回收,这不是我想要的。
首先,我使用了这个示例。它可以工作,但是为每个缓存实例创建一个线程使我感到烦恼,它使用了强健的键引用,并且自己删除了陈旧的条目(例如WeakHashMap),这在缓存类的某些方法调用中不起作用(显然),当我冒着风险运行时当我不打电话给他们时,内存不足。我想使用Guava,但MapMaker不再允许使用逻辑上合理的软键,因为默认等价基于等式(==)而不是基于equals()方法,这意味着无法重新创建相同的键。但是,我同意sebastien-lorber的评论:
i think soft keys would make sense if Guava was overriding equals method of the SoftReference. I've seen that Guava uses an"equivalence mecanism" and i think, for soft references, the defaultEquivalence should not be identity but equals, to handle such a case.
我也查看了MapDB和JCS。
如何修改上面引用的示例或使用Guava基于软引用创建缓存,最好使用equals()而不是==来确定键的相等性?
我猜唯一的完美解决方案是按照您的建议在Guava中添加CacheBuilder.softKeysDelegatingEquals。
跳到最后一个更好的主意
也许此解决方法可以做到:
每当在Cache中放入内容时,都将新的SoftReference放入自己的HashSet中。
在Cache中添加removalListener并将SoftReference从HashSet中删除
无论如何,您不需要线程。
第二部分有些棘手,因为您得到了Key并且需要删除其相应的软引用。为此,您可以将集合替换为WeakHashMap,也可以通过将密钥为equals用作引用来变黑:
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 {
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()
并在Value中添加SoftReference。这使得密钥可以轻而易举地到达。
如果您确实需要比较是平等的,那么您可能很不走运,尽管WeakInterner可能会有所帮助。
您的最后一个解决方案似乎非常有趣。 虐待尝试一下。 非常感谢您的完整答复。 病给你一些反馈。
实际上,我只是在Guava中重新引入了软键(在版本14中已将其删除),但在CacheBuilder中:
https://code.google.com/p/guava-libraries/issues/detail?id=1845
然后,我必须调用非公共方法CacheBuilder.keyEquivalence(Equivalence.equals())才能使用equals()而不是==来比较键。我会让你知道它是否有效。在这种情况下,我将接受自己的回答。否则,我将不得不使用maaartinus的建议。