package com.circlegate.liban.base; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map.Entry; import com.circlegate.liban.base.CommonClasses.EntryImpl; public class CustomCollections { public interface ICache<TKey, TValue> { int size(); boolean containsKey(TKey key); void put(TKey key, TValue value); TValue get(TKey key); TValue remove(TKey key); void clear(); List<Entry<TKey, TValue>> generateAll(); ISynchronizedCache<TKey, TValue> getSynchronizedCache(); } public interface ISynchronizedCache<TKey, TValue> extends ICache<TKey, TValue> { Object getLock(); } public static class SynchronizedCache<TKey, TValue> implements ISynchronizedCache<TKey, TValue> { private final ICache<TKey, TValue> wrappedCache; public SynchronizedCache(ICache<TKey, TValue> wrappedCache) { this.wrappedCache = wrappedCache; } @Override public synchronized int size() { return wrappedCache.size(); } @Override public synchronized boolean containsKey(TKey key) { return wrappedCache.containsKey(key); } @Override public synchronized void put(TKey key, TValue value) { wrappedCache.put(key, value); } @Override public synchronized TValue get(TKey key) { return wrappedCache.get(key); } @Override public synchronized TValue remove(TKey key) { return wrappedCache.remove(key); } @Override public synchronized void clear() { wrappedCache.clear(); } @Override public synchronized List<Entry<TKey, TValue>> generateAll() { return wrappedCache.generateAll(); } @Override public ISynchronizedCache<TKey, TValue> getSynchronizedCache() { return this; } @Override public Object getLock() { return this; } } /** * An LRU cache, based on <code>LinkedHashMap</code>. * * <p> * This cache has a fixed maximum number of elements (<code>cacheSize</code> * ). If the cache is full and another entry is added, the LRU (least * recently used) entry is dropped. * <p> */ public static class LRUCache<TKey, TValue> implements ICache<TKey, TValue> { private static final float hashTableLoadFactor = 0.75f; private LinkedHashMap<TKey, TValue> map; private int cacheSize; /** * Creates a new LRU cache. * * @param cacheSize * the maximum number of entries that will be kept in this * cache. */ public LRUCache(int cacheSize) { this.cacheSize = cacheSize; int hashTableCapacity = (int) Math.ceil(cacheSize / hashTableLoadFactor) + 1; map = new LinkedHashMap<TKey, TValue>(hashTableCapacity, hashTableLoadFactor, true) { // (an anonymous inner class) private static final long serialVersionUID = 1; @Override protected boolean removeEldestEntry(Entry<TKey, TValue> eldest) { return size() > LRUCache.this.cacheSize; } }; } @Override public int size() { return map.size(); } @Override public boolean containsKey(TKey key) { return map.containsKey(key); } /** * Retrieves an entry from the cache.<br> * The retrieved entry becomes the MRU (most recently used) entry. * * @param key * the key whose associated value is to be returned. * @return the value associated to this key, or null if no value with * this key exists in the cache. */ @Override public TValue get(TKey key) { return map.get(key); } /** * Adds an entry to this cache. The new entry becomes the MRU (most * recently used) entry. If an entry with the specified key already * exists in the cache, it is replaced by the new entry. If the cache is * full, the LRU (least recently used) entry is removed from the cache. * * @param key * the key with which the specified value is to be * associated. * @param value * a value to be associated with the specified key. */ @Override public void put(TKey key, TValue value) { map.put(key, value); } /** * Clears the cache. */ @Override public void clear() { map.clear(); } @Override public TValue remove(TKey key) { return map.remove(key); } @Override public List<Entry<TKey, TValue>> generateAll() { return new ArrayList<Entry<TKey, TValue>>(map.entrySet()); } @Override public ISynchronizedCache<TKey, TValue> getSynchronizedCache() { return new SynchronizedCache<TKey, TValue>(this); } } public static class CacheWeakRef<TKey, TValue> implements ICache<TKey, TValue> { private final HashMap<TKey, WeakReference<TValue>> cache = new HashMap<TKey, WeakReference<TValue>>(); private int counter = 0; @Override public int size() { return cache.size(); } @Override public boolean containsKey(TKey key) { WeakReference<TValue> w = cache.get(key); return w != null && w.get() != null; } @Override public void put(TKey key, TValue value) { // v kazdem pripade chci, aby v cachi byla posledni hodnota value if (cache.put(key, new WeakReference<TValue>(value)) == null) { if (counter++ >= 100) { removeDeadReferences(); counter = 0; } } } @Override public TValue get(TKey key) { WeakReference<TValue> w = cache.get(key); return w != null ? w.get() : null; } @Override public TValue remove(TKey key) { WeakReference<TValue> w = cache.remove(key); return w != null ? w.get() : null; } @Override public void clear() { cache.clear(); } @Override public List<Entry<TKey, TValue>> generateAll() { ArrayList<Entry<TKey, TValue>> ret = new ArrayList<Entry<TKey,TValue>>(); for (Entry<TKey, WeakReference<TValue>> entry : cache.entrySet()) { TValue v = entry.getValue().get(); if (v != null) ret.add(new EntryImpl<TKey, TValue>(entry.getKey(), v)); } return ret; } @Override public ISynchronizedCache<TKey, TValue> getSynchronizedCache() { return new SynchronizedCache<TKey, TValue>(this); } void removeDeadReferences() { for (Iterator<Entry<TKey, WeakReference<TValue>>> it = cache .entrySet().iterator(); it.hasNext();) { Entry<TKey, WeakReference<TValue>> entry = it.next(); if (entry.getValue().get() == null) { it.remove(); } } } } }