3

我想我是另一个试图用 Wea​​kHashMap 做某种缓存的人。我需要一些帮助。

我有一堆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()方法公开,这解决了问题,但这是一个糟糕的 hack :(

PS。我知道 apache 或 google 集合可能有这样的东西,但我真的不想添加 2Mb 依赖项。

4

2 回答 2

2

我建议替换WeakReferencesSoftReferences.

仅由 a 引用的任何对象都是WeakReference垃圾收集器每一轮的目标。这意味着即使仍有足够的可用内存,也可以清除缓存。

如果替换WeakReference为,SoftReference则声明:仅在绝对没有可用内存可分配时才删除引用的对象。

java中没有现成的SoftHashMap实现。番石榴中有一个很好的例子 - MapMaker。值得在生产环境代码中使用这种经过良好测试和验证的代码,而不是提供您自己的质量肯定较低的实现。它还具有惊人的“自清洁”机制:

  1. 您可以指定缓存最大大小

    随着地图大小接近最大值,地图将驱逐不太可能再次使用的条目。例如,地图可能会驱逐一个条目,因为它最近或不经常使用。

  2. expireAfterWrite您可以使用和expireAfterAccess方法指定映射条目的到期时间。

我也发现你的缓存设计不是很方便。正如我从您的代码片段中了解到的那样,从一开始您Track的 s 就对它们有很强的引用,TrackData并且您在这些情况下构建缓存。但是从某个时刻开始,您想使用缓存来检索数据,因此您必须以Track其他方式创建新的,因为从那一刻起您想使用缓存而不是强引用。

不同Tracks的可以有相同的TrackData,所以我们不能Track用作键。所以,我会采用下一种方法:

  1. 引入中间 ids 级别并基于Map<Integer, TrackData>具有软值和定义的自清理策略进行缓存(基于MapMaker);
  2. 改变与Track --> TrackData的关系Track --> Id (int)。缓存Id --> TrackData
于 2010-12-05T09:51:20.347 回答
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对象的构造总是作为读取文件的一部分发生,但是创建的实例被丢弃以支持共享实例,我会这样建模:

 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. 这可以通过 make WeakReferenceinside来解决Track- 但是这也意味着您最终可能没有任何TrackData,并且需要从文件中读取它,在这种情况下,第一个解决方案比第二个解决方案更好地建模。

于 2010-12-04T18:30:02.343 回答