package freenet.support; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; /** * LRU Queue * * push()'ing an existing object move it to tail, no duplicated object are ever added. */ public class LRUQueue<T> { /* * I've just converted this to using the DLList and Hashtable * this makes it Hashtable time instead of O(N) for push and * remove, and Hashtable time instead of O(1) for pop. Since * push is by far the most done operation, this should be an * overall improvement. */ private final DoublyLinkedListImpl<QItem<T>> list = new DoublyLinkedListImpl<QItem<T>>(); private final Map<T, QItem<T>> hash = new HashMap<T, QItem<T>>(); public LRUQueue() { } /** * push()ing an object that is already in * the queue moves that object to the most * recently used position, but doesn't add * a duplicate entry in the queue. */ public final synchronized void push(T obj) { if (obj == null) throw new NullPointerException(); QItem<T> insert = hash.get(obj); if (insert == null) { insert = new QItem<T>(obj); hash.put(obj,insert); } else { list.remove(insert); } list.unshift(insert); } /** * push to bottom (least recently used position) */ public synchronized void pushLeast(T obj) { if (obj == null) throw new NullPointerException(); QItem<T> insert = hash.get(obj); if (insert == null) { insert = new QItem<T>(obj); hash.put(obj,insert); } else { list.remove(insert); } list.push(insert); } /** * @return Least recently pushed Object. */ public final synchronized T pop() { if ( list.size() > 0 ) { return hash.remove(list.pop().obj).obj; } else { return null; } } public final int size() { return list.size(); } public final synchronized boolean remove(Object obj) { if (obj == null) throw new NullPointerException(); QItem<T> i = hash.remove(obj); if(i != null) { list.remove(i); return true; } else { return false; } } /** * Check if this queue contains obj * @param obj Object to match * @return true if this queue contains obj. */ public final synchronized boolean contains(Object obj) { return hash.containsKey(obj); } public Enumeration<T> elements() { return new ItemEnumeration(); } private class ItemEnumeration implements Enumeration<T> { private Enumeration<QItem<T>> source = list.reverseElements(); @Override public boolean hasMoreElements() { return source.hasMoreElements(); } @Override public T nextElement() { return source.nextElement().obj; } } private static class QItem<T> extends DoublyLinkedListImpl.Item<QItem<T>> { public T obj; public QItem(T obj) { this.obj = obj; } } /** * Return the objects in the queue as an array in an arbitrary and meaningless * order. */ public synchronized Object[] toArray() { return hash.keySet().toArray(); } /** * Return the objects in the queue as an array in an arbitrary and meaningless * order. * @param array The array to fill in. If it is too small a new array of the same type will be allocated. */ public synchronized <E> E[] toArray(E[] array) { return hash.keySet().toArray(array); } /** * Return the objects in the queue as an array. The <strong>least</strong> * recently used object is in <tt>[0]</tt>, the <strong>most</strong> * recently used object is in <tt>[array.length-1]</tt>. */ public synchronized Object[] toArrayOrdered() { Object[] array = new Object[list.size()]; int x = 0; for (Enumeration<QItem<T>> e = list.reverseElements(); e.hasMoreElements();) { array[x++] = e.nextElement().obj; } return array; } /** * Return the objects in the queue as an array. The <strong>least</strong> * recently used object is in <tt>[0]</tt>, the <strong>most</strong> * recently used object is in <tt>[array.length-1]</tt>. * * @param array * The array to fill in. If it is too small a new array of the * same type will be allocated. */ @SuppressWarnings("unchecked") public synchronized <E> E[] toArrayOrdered(E[] array) { array = toArray(array); int listSize = list.size(); if(array.length != listSize) throw new IllegalStateException("array.length="+array.length+" but list.size="+listSize); int x = 0; for (Enumeration<QItem<T>> e = list.reverseElements(); e.hasMoreElements();) { array[x++] = (E) e.nextElement().obj; } return array; } public synchronized boolean isEmpty() { return hash.isEmpty(); } public synchronized void clear() { list.clear(); hash.clear(); } public synchronized T get(T obj) { QItem<T> val = hash.get(obj); if(val == null) return null; return val.obj; } }