package org.andengine.util.adt.cache; import java.util.HashMap; import org.andengine.util.adt.pool.GenericPool; /** * (c) Zynga 2011 * * @author Nicolas Gramlich <ngramlich@zynga.com> * @since 00:24:52 - 02.11.2011 */ public class LRUCache<K, V> { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== private final int mCapacity; private int mSize; private final HashMap<K, LRUCacheValueHolder<K, V>> mMap; private final LRUCacheQueue<K> mLRUCacheQueue; private final GenericPool<LRUCacheValueHolder<K, V>> mLRUCacheValueHolderPool = new GenericPool<LRUCache.LRUCacheValueHolder<K, V>>() { @Override protected LRUCacheValueHolder<K, V> onAllocatePoolItem() { return new LRUCacheValueHolder<K, V>(); } @Override protected void onHandleRecycleItem(final LRUCacheValueHolder<K, V> pLRUCacheValueHolder) { pLRUCacheValueHolder.mLRUCacheQueueNode = null; pLRUCacheValueHolder.mValue = null; } }; // =========================================================== // Constructors // =========================================================== public LRUCache(final int pCapacity) { this.mCapacity = pCapacity; this.mMap = new HashMap<K, LRUCacheValueHolder<K, V>>(pCapacity); this.mLRUCacheQueue = new LRUCacheQueue<K>(); } // =========================================================== // Getter & Setter // =========================================================== public int getCapacity() { return this.mCapacity; } public int getSize() { return this.mSize; } // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== public boolean isEmpty() { return this.mSize == 0; } public V put(final K pKey, final V pValue) { final LRUCacheValueHolder<K, V> existingLRUCacheValueHolder = this.mMap.get(pKey); if(existingLRUCacheValueHolder != null) { /* Just heat up that item. */ this.mLRUCacheQueue.moveToTail(existingLRUCacheValueHolder.mLRUCacheQueueNode); return existingLRUCacheValueHolder.mValue; } if(this.mSize >= this.mCapacity) { final K deadKey = this.mLRUCacheQueue.poll(); this.mMap.remove(deadKey); this.mSize--; } final LRUCacheQueueNode<K> lruCacheQueueNode = this.mLRUCacheQueue.add(pKey); final LRUCacheValueHolder<K, V> lruCacheValueHolder = this.mLRUCacheValueHolderPool.obtainPoolItem(); // final LRUCacheValueHolder<K, V> lruCacheValueHolder = new LRUCacheValueHolder<K, V>(); lruCacheValueHolder.mValue = pValue; lruCacheValueHolder.mLRUCacheQueueNode = lruCacheQueueNode; this.mMap.put(pKey, lruCacheValueHolder); this.mSize++; return null; } public V get(final K pKey) { final LRUCacheValueHolder<K, V> lruCacheValueHolder = this.mMap.get(pKey); if(lruCacheValueHolder == null) { return null; } this.mLRUCacheQueue.moveToTail(lruCacheValueHolder.mLRUCacheQueueNode); return lruCacheValueHolder.mValue; } public void clear() { while(!this.mLRUCacheQueue.isEmpty()) { final K key = this.mLRUCacheQueue.poll(); final LRUCacheValueHolder<K, V> lruCacheValueHolder = this.mMap.remove(key); this.mLRUCacheValueHolderPool.recyclePoolItem(lruCacheValueHolder); } this.mSize = 0; } // =========================================================== // Methods // =========================================================== // =========================================================== // Inner and Anonymous Classes // =========================================================== static class LRUCacheQueueNode<K> { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== K mKey; LRUCacheQueueNode<K> mPrevious; LRUCacheQueueNode<K> mNext; // =========================================================== // Constructors // =========================================================== // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== // =========================================================== // Methods // =========================================================== // =========================================================== // Inner and Anonymous Classes // =========================================================== } static class LRUCacheValueHolder<K, V> { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== V mValue; LRUCacheQueueNode<K> mLRUCacheQueueNode; // =========================================================== // Constructors // =========================================================== // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== // =========================================================== // Methods // =========================================================== // =========================================================== // Inner and Anonymous Classes // =========================================================== } static class LRUCacheQueue<K> { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== private LRUCacheQueueNode<K> mHead; private LRUCacheQueueNode<K> mTail; private final GenericPool<LRUCacheQueueNode<K>> mLRUCacheQueueNodePool = new GenericPool<LRUCache.LRUCacheQueueNode<K>>() { @Override protected LRUCacheQueueNode<K> onAllocatePoolItem() { return new LRUCacheQueueNode<K>(); } @Override protected void onHandleRecycleItem(final LRUCacheQueueNode<K> pLRUCacheQueueNode) { pLRUCacheQueueNode.mKey = null; pLRUCacheQueueNode.mPrevious = null; pLRUCacheQueueNode.mNext = null; } }; // =========================================================== // Constructors // =========================================================== // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== // =========================================================== // Methods // =========================================================== public boolean isEmpty() { return this.mHead == null; } public LRUCacheQueueNode<K> add(final K pKey) { final LRUCacheQueueNode<K> lruCacheQueueNode = this.mLRUCacheQueueNodePool.obtainPoolItem(); // final LRUCacheQueueNode<K> lruCacheQueueNode = new LRUCacheQueueNode<K>(); lruCacheQueueNode.mKey = pKey; return this.add(lruCacheQueueNode); } private LRUCacheQueueNode<K> add(final LRUCacheQueueNode<K> pLRUCacheQueueNode) { if(this.isEmpty()) { this.mHead = pLRUCacheQueueNode; this.mTail = this.mHead; } else { this.mTail.mNext = pLRUCacheQueueNode; pLRUCacheQueueNode.mPrevious = this.mTail; this.mTail = pLRUCacheQueueNode; } return this.mTail; } public K poll() { final LRUCacheQueueNode<K> head = this.mHead; final K key = this.mHead.mKey; /* Check if item to poll is the tail. */ if(this.mHead.mNext == null) { this.mHead = null; this.mTail = null; } else { this.mHead = this.mHead.mNext; this.mHead.mPrevious = null; } this.mLRUCacheQueueNodePool.recyclePoolItem(head); return key; } public void moveToTail(final LRUCacheQueueNode<K> pLRUCacheQueueNode) { final LRUCacheQueueNode<K> next = pLRUCacheQueueNode.mNext; /* Check if the node already is the tail. */ if(next == null) { return; } else { final LRUCacheQueueNode<K> previous = pLRUCacheQueueNode.mPrevious; next.mPrevious = previous; /* Check if item to bump is the head. */ if(previous == null) { this.mHead = next; } else { previous.mNext = next; } this.mTail.mNext = pLRUCacheQueueNode; pLRUCacheQueueNode.mPrevious = this.mTail; pLRUCacheQueueNode.mNext = null; this.mTail = pLRUCacheQueueNode; } } // =========================================================== // Inner and Anonymous Classes // =========================================================== } }