我正在使用MapMaker在我的应用程序中实现数据对象的缓存:
public class DataObjectCache<DO extends MyDataObject> {
private final ConcurrentMap<String, DO> innerCache;
public DataObjectCache(Class<DO> doClass) {
Function<String, DO> loadFunction = new Function<String, DO>() {
@Override
public DO apply(String id) {
//load and return DO instance
}
};
innerCache = new MapMaker()
.softValues()
.makeComputingMap(loadFunction);
}
private DO getDataObject(String id) {
return innerCache.get(id);
}
private void putDataObject(DO dataObject) {
innerCache.putIfAbsent(dataObject.getID(), dataObject);
}
}
将为每个数据对象类实例化其中一个DataObjectCaches,并将它们保存在主映射中,使用Class对象作为键。
有少数数据对象类,其实例我不想缓存。但是,我仍然希望它们可以通过函数调用的相同代码进行实例化,并且在加载它们时仍然需要并发性。
在这些情况下,我想知道我是否可以将地图的最大大小设置为0,以便立即驱逐条目,但仍然利用地图的原子计算方面。这是一个好主意吗?低效?
修改
我意识到如果我在加载后立即驱逐条目,就无法保证它们被明显加载 - 如果Map没有跟踪它们,那么具有相同ID的对象的多个实例可能会浮动在环境。所以我没有这样做,我认为我会使用弱值而不是软值来表示我不想占用缓存的对象类型 - 如果有人对此有任何意见,请告诉我。
答案 0 :(得分:2)
根据您的编辑,听起来您正在寻找的是一个内部人。 interner返回一个代表性实例; Interner.intern
将根据您的equals
方法为所有相等的对象返回相同的对象。来自Javadoc:
选择并返回代表 任何一个集合的实例 每个实例等于每个实例 其他。如果给出两个相等的输入 对于这种方法,两个调用都将返回 相同的实例。那是, 实习生(a).equals(a)总是持有,和 实习生(a)==实习生(b)当且仅当 a.equals(b)中。注意实习生(a)是 允许现在返回一个实例 以后如果是另一个实例 原来的实习案例是 垃圾收集。
请参阅http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Interner.html和http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Interners.html
那就是说,当你说你不希望它被缓存时,这取决于你的意思。如果你真的想每次都返回一个新实例,那么你必须有多个等效对象实例“浮动”。
Interning正在持有一个实例(因此它可以返回相同的实例),因此它仍然是一种缓存。我想知道你为什么要避免缓存。如果是因为物体的大小,你可以使用弱的内部物;当不再引用GC时,该实例将可用于GC。然后,简单地使用具有弱值的MapMaker
地图也可以实现这一点。
另一方面,如果您不想缓存的原因是因为您的数据可能会发生变化,那么实习可能就是您的答案。我想你想要的是每次检索对象,然后实习它。如果对象等于缓存的对象,则interner将只返回现有实例。如果不同,则interner将缓存新的。那么您的责任就是在对象上编写一个equals
方法,该方法符合使用新的实例与实例的要求。
答案 1 :(得分:0)
好吧,MapMaker.maximumSize
有这一行:checkArgument(size > 0, "maximum size must be positive")
,这将使这个变得不可能。 expireAfter
方法也需要积极的论据。很明显,API设计人员不希望您以这种方式使用他们的MapMaker
制作的地图。
那就是说,我想,如果你真的想要使用缓存作为直通,你可以使用1纳秒的expireAfterWrite
。这是一个黑客,但实际上会产生同样的效果。