Java: нужен совет по поводу WeakHashMap

Я думаю, что я еще один человек, пытающийся создать какой-то кеш с помощью 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() для проверки местоположения и индекса подпесни). Я хочу использовать слабые ссылки, чтобы мне не нужно было беспокоиться об удалении дорожек.

Я хотел спросить, можно ли хранить слабую ссылку на ключ в WeakHashMap, и если нет, то как мне решить эту проблему? Мне нужны слабые ссылки и постоянное извлечение кэшированных значений. Я думал скопировать код WeakHashMap и сделать общедоступным метод getEntry(), который решает проблему, но это такой плохой хак :(

PS. Я понимаю, что в коллекциях apache или google может быть что-то подобное, но я действительно не хочу добавлять зависимости 2Mb.


person Denis Tulskiy    schedule 04.12.2010    source источник
comment
Вам нужен WeakSet? Прочитайте этот stackoverflow.com/ вопросы/4062919/   -  person Stan Kurilin    schedule 04.12.2010
comment
@Стас Курилин: Мне нужно иметь возможность получать объекты без итерации, поэтому Set здесь не работает.   -  person Denis Tulskiy    schedule 05.12.2010


Ответы (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. Это можно исправить, сделав WeakReference внутри Track, однако это также означает, что у вас может не быть TrackData, и вам нужно будет прочитать его обратно из файла, и в этом случае первое решение лучше смоделировано, чем второе.

person Martin Algesten    schedule 04.12.2010
comment
Привет, спасибо за ваш ответ. getTrackData просто добытчик, стоит не дорого. Мне нужен этот кеш, потому что несколько треков могут указывать на одни и те же данные TrackData, т. е. трек — это просто запись в списке воспроизведения, поэтому у меня может быть несколько копий трека, указывающих на одни и те же TrackData. Поэтому мне нужен кеш, чтобы иметь возможность делиться объектами, а не ускорять время доступа. - person Denis Tulskiy; 05.12.2010
comment
@тульский, хм. Добавляете ли вы TrackData в Track с помощью конструктора/установщика или он вычисляет с использованием других полей? - person Stan Kurilin; 05.12.2010
comment
@Стас Курилин: он создается, когда я читаю теги и другую информацию из файла. - person Denis Tulskiy; 05.12.2010
comment
Спасибо, я посмотрю, смогу ли я переопределить метод equals Tracks. Иногда мне нужно полагаться на разные экземпляры треков с одинаковыми данными, чтобы они были разными. - person Denis Tulskiy; 06.12.2010