package org.andengine.util.adt.cache; import org.andengine.util.adt.pool.GenericPool; import android.util.SparseArray; /** * (c) Zynga 2011 * * @author Nicolas Gramlich <ngramlich@zynga.com> * @since 12:19:22 - 08.12.2011 */ public class IntLRUCache<V> { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== private final int mCapacity; private int mSize; private final SparseArray<IntLRUCacheValueHolder<V>> mSparseArray; private final IntLRUCacheQueue mIntLRUCacheQueue; private final GenericPool<IntLRUCacheValueHolder<V>> mIntLRUCacheValueHolderPool = new GenericPool<IntLRUCache.IntLRUCacheValueHolder<V>>() { @Override protected IntLRUCacheValueHolder<V> onAllocatePoolItem() { return new IntLRUCacheValueHolder<V>(); } @Override protected void onHandleRecycleItem(final IntLRUCacheValueHolder<V> pIntLRUCacheValueHolder) { pIntLRUCacheValueHolder.mIntLRUCacheQueueNode = null; pIntLRUCacheValueHolder.mValue = null; } }; // =========================================================== // Constructors // =========================================================== public IntLRUCache(final int pCapacity) { this.mCapacity = pCapacity; this.mSparseArray = new SparseArray<IntLRUCacheValueHolder<V>>(pCapacity); this.mIntLRUCacheQueue = new IntLRUCacheQueue(); } // =========================================================== // 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 int pKey, final V pValue) { final IntLRUCacheValueHolder<V> existingIntLRUCacheValueHolder = this.mSparseArray.get(pKey); if(existingIntLRUCacheValueHolder != null) { /* Just heat up that item. */ this.mIntLRUCacheQueue.moveToTail(existingIntLRUCacheValueHolder.mIntLRUCacheQueueNode); return existingIntLRUCacheValueHolder.mValue; } if(this.mSize >= this.mCapacity) { final int deadKey = this.mIntLRUCacheQueue.poll(); this.mSparseArray.remove(deadKey); this.mSize--; } final IntLRUCacheQueueNode IntLRUCacheQueueNode = this.mIntLRUCacheQueue.add(pKey); final IntLRUCacheValueHolder<V> IntLRUCacheValueHolder = this.mIntLRUCacheValueHolderPool.obtainPoolItem(); // final IntLRUCacheValueHolder<V> IntLRUCacheValueHolder = new IntLRUCacheValueHolder<V>(); IntLRUCacheValueHolder.mValue = pValue; IntLRUCacheValueHolder.mIntLRUCacheQueueNode = IntLRUCacheQueueNode; this.mSparseArray.put(pKey, IntLRUCacheValueHolder); this.mSize++; return null; } public V get(final int pKey) { final IntLRUCacheValueHolder<V> IntLRUCacheValueHolder = this.mSparseArray.get(pKey); if(IntLRUCacheValueHolder == null) { return null; } this.mIntLRUCacheQueue.moveToTail(IntLRUCacheValueHolder.mIntLRUCacheQueueNode); return IntLRUCacheValueHolder.mValue; } public void clear() { while(!this.mIntLRUCacheQueue.isEmpty()) { final int key = this.mIntLRUCacheQueue.poll(); final IntLRUCacheValueHolder<V> lruCacheValueHolder = this.mSparseArray.get(key); if(lruCacheValueHolder == null) { throw new IllegalArgumentException(); } this.mSparseArray.remove(key); this.mIntLRUCacheValueHolderPool.recyclePoolItem(lruCacheValueHolder); } this.mSize = 0; } // =========================================================== // Methods // =========================================================== // =========================================================== // Inner and Anonymous Classes // =========================================================== static class IntLRUCacheQueueNode { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== int mKey; IntLRUCacheQueueNode mPrevious; IntLRUCacheQueueNode mNext; // =========================================================== // Constructors // =========================================================== // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== // =========================================================== // Methods // =========================================================== // =========================================================== // Inner and Anonymous Classes // =========================================================== } static class IntLRUCacheValueHolder<V> { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== V mValue; IntLRUCacheQueueNode mIntLRUCacheQueueNode; // =========================================================== // Constructors // =========================================================== // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== // =========================================================== // Methods // =========================================================== // =========================================================== // Inner and Anonymous Classes // =========================================================== } static class IntLRUCacheQueue { // =========================================================== // Constants // =========================================================== // =========================================================== // Fields // =========================================================== private IntLRUCacheQueueNode mHead; private IntLRUCacheQueueNode mTail; private final GenericPool<IntLRUCacheQueueNode> mIntLRUCacheQueueNodePool = new GenericPool<IntLRUCache.IntLRUCacheQueueNode>() { @Override protected IntLRUCacheQueueNode onAllocatePoolItem() { return new IntLRUCacheQueueNode(); } @Override protected void onHandleRecycleItem(final IntLRUCacheQueueNode pIntLRUCacheQueueNode) { pIntLRUCacheQueueNode.mKey = 0; pIntLRUCacheQueueNode.mPrevious = null; pIntLRUCacheQueueNode.mNext = null; } }; // =========================================================== // Constructors // =========================================================== // =========================================================== // Getter & Setter // =========================================================== // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== // =========================================================== // Methods // =========================================================== public boolean isEmpty() { return this.mHead == null; } public IntLRUCacheQueueNode add(final int pKey) { final IntLRUCacheQueueNode IntLRUCacheQueueNode = this.mIntLRUCacheQueueNodePool.obtainPoolItem(); // final IntLRUCacheQueueNode IntLRUCacheQueueNode = new IntLRUCacheQueueNode(); IntLRUCacheQueueNode.mKey = pKey; return this.add(IntLRUCacheQueueNode); } private IntLRUCacheQueueNode add(final IntLRUCacheQueueNode pIntLRUCacheQueueNode) { if(this.isEmpty()) { this.mHead = pIntLRUCacheQueueNode; this.mTail = this.mHead; } else { this.mTail.mNext = pIntLRUCacheQueueNode; pIntLRUCacheQueueNode.mPrevious = this.mTail; this.mTail = pIntLRUCacheQueueNode; } return this.mTail; } public int poll() { final IntLRUCacheQueueNode head = this.mHead; final int key = this.mHead.mKey; if(key == 0) { throw new IllegalStateException(); } /* 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.mIntLRUCacheQueueNodePool.recyclePoolItem(head); return key; } public void moveToTail(final IntLRUCacheQueueNode pIntLRUCacheQueueNode) { final IntLRUCacheQueueNode next = pIntLRUCacheQueueNode.mNext; /* Check if the node already is the tail. */ if(next == null) { return; } else { final IntLRUCacheQueueNode previous = pIntLRUCacheQueueNode.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 = pIntLRUCacheQueueNode; pIntLRUCacheQueueNode.mPrevious = this.mTail; pIntLRUCacheQueueNode.mNext = null; this.mTail = pIntLRUCacheQueueNode; } } // =========================================================== // Inner and Anonymous Classes // =========================================================== } }