我想我是另一个尝试使用WeakHashMap创建某种缓存的人。我需要一些帮助。
我有一堆TrackData
个对象,其中包含有关音轨的信息。然后有Track
个对象引用内部的TrackData
。多个曲目可以指向相同的TrackData
。然后我有TrackDataCache
类看起来像这样:
public class TrackDataCache {
private static TrackDataCache instance = new TrackDataCache();
public static TrackDataCache getInstance() {
return instance;
}
private WeakHashMap<TrackData, WeakReference<TrackData>> cache = new WeakHashMap<TrackData, WeakReference<TrackData>>();
public void cache(Track track) {
TrackData key = track.getTrackData();
WeakReference<TrackData> trackData = cache.get(key);
if (trackData == null) {
cache.put(key, new WeakReference<TrackData>(key));
} else {
track.setTrackData(trackData.get());
}
}
}
因此,当我加载一个曲目时,我调用TrackDataCache.cache()
并且如果之前没有加载它的跟踪数据,则将其缓存或替换为缓存副本,否则(TrackData
会覆盖equals()方法以检查位置和子索引)。我想使用弱引用,以便在删除Tracks时不需要关心。
我想问一下,在WeakHashMap中保持对密钥的弱引用是否可行,如果没有,我该如何处理这个问题?我需要弱引用和持续时间检索缓存值。我正在考虑复制WeakHashMap代码并将getEntry()
方法公开,这解决了问题,但它是如此糟糕的黑客:(
PS。我知道apache或google集合可能有这样的东西,但我真的不想添加2Mb依赖项。
答案 0 :(得分:2)
我建议将WeakReferences
替换为SoftReferences
。
仅由WeakReference
引用的任何对象都是每轮垃圾收集器的目标。这意味着即使存在足够的可用内存,也可以清除缓存。
如果用WeakReference
替换SoftReference
,则说明:只有在绝对没有可分配的可用内存时才删除引用的对象。
java中没有现成的SoftHashMap
实现。番石榴有一个很好的例子 - MapMaker
。值得使用经过充分测试和验证的生产环境代码,而不是提供您自己的质量较低的实现。它还具有惊人的“自我清洁”机制:
随着地图大小越来越接近 最大值,地图将逐出条目 不太可能再次使用。 例如,地图可能会逐出 因为尚未使用而进入 最近或经常。
expireAfterWrite
和expireAfterAccess
方法为地图条目指定到期时间。我也发现你的缓存设计不太方便。据我所知,从您的代码段开始,您的Track
开始强烈引用他们的TrackData
,并在这些情况下构建缓存。但是从某些时刻开始,您希望使用缓存来检索数据,因此您必须以其他方式创建新的Track
,因为从那个时刻开始想要使用缓存而不是强引用。
不同的Tracks
可以具有相同的TrackData
,因此我们无法将Track
用作关键字。所以,我会选择下一个方法:
Map<Integer, TrackData>
和定义的自清洁策略(基于MapMaker
)制作缓存; Track --> TrackData
更改为Track --> Id (int)
。缓存Id --> TrackData
。答案 1 :(得分:1)
TrackData
可以由Track
的许多实例共享。我们需要一个不需要TrackData
来获取多个Track
的相同实例的密钥系统。
public class Track [
@Override
public int hashcode() {
... make hashcode that will be the same for
... tracks sharing the same track data.
}
@Override
public boolean equals() {
... ensure that if A.hashcode == B.hashcode then A.equals(B)
}
}
public class TrackDataManager {
private WeakHashMap<Track,TrackData> cache = new WeakHashMap<Track,TrackData>();
public TrackData getTrackData(Track track) {
// Track.hashcode()/equals() ensures two tracks that
// share track data will get the same object back
TrackData data = cache.get(track);
if (data == null) {
data = constructDataFromTrackFile(track);
cache.put(track, data);
}
return data;
}
private TrackData constructDataFromTrackFile(Track track) {
... read data from file and create that object.
}
}
如果TrackData
对象的构造总是作为读取文件的一部分发生,但是创建的实例被抛弃而偏向于共享实例,我会像这样建模:< / p>
public class TrackData {
@Override
public int hashcode() {
... make hashcode that will be the same for same track data.
}
@Override
public boolean equals() {
... ensure that if A.hashcode == B.hashcode then A.equals(B)
}
}
public class TrackDataCache {
private WeakHashMap<Integer,TrackData> cache = new WeakHashMap<Integer,TrackData>();
public TrackData getTrackData(Track track) {
// cache contains shared TrackData instances, we may throw away
// the Track instance in favour of the shared one.
Integer key = track.getTrackData().hashcode();
TrackData data = cache.get(key);
if (data == null) {
cache.put(key, track.getTrackData());
data = track.getTrackData();
} else {
// ensure we're using the shared instance, not the local one.
// deliberate object reference comparison
if (data != track.getTrackData()) {
track.setTrackData(data);
}
}
return data;
}
}
请注意,WeakHashMap
只要有Track
个对象保持对TrackData
的引用,就不会在这两个解决方案中的任何一个中执行任何操作。这可以通过在WeakReference
内设置Track
来解决 - 但这也意味着您最终可能没有TrackData
,并且需要从文件中读取它,在这种情况下,第一个解决方案比第二种模型更好。