package freenet.support; import java.util.HashMap; import java.util.Iterator; import java.util.Set; import java.util.TreeSet; /** * Variant on LRUHashtable which provides an efficient how-many-since-time-T operation. */ public class TimeSortedHashtable> { public TimeSortedHashtable() { this.elements = new TreeSet>(); this.valueToElement = new HashMap>(); } private static class Element> implements Comparable> { Element(long t, T v) { time = t; value = v; } long time; final T value; public int compareTo(Element o) { if(time > o.time) return 1; if(time < o.time) return -1; if (value == null && o.value == null) return 0; if (value == null && o.value != null) return 1; if (value != null && o.value == null) return -1; return value.compareTo(o.value); } } private final TreeSet> elements; private final HashMap> valueToElement; /** * 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. * @param now */ public final synchronized void push(T value, long now) { assert(elements.size() == valueToElement.size()); if (value == null) throw new NullPointerException(); Element e = valueToElement.get(value); if(e == null) { e = new Element(now, value); elements.add(e); valueToElement.put(value, e); } else { elements.remove(e); e.time = now; elements.add(e); } assert(elements.size() == valueToElement.size()); } public final int size() { return elements.size(); } public final synchronized boolean removeValue(T value) { assert(elements.size() == valueToElement.size()); Element e = valueToElement.remove(value); if(e == null) return false; elements.remove(e); assert(elements.size() == valueToElement.size()); return true; } public final synchronized boolean containsValue(T key) { return valueToElement.containsKey(key); } /** * Note that this does not automatically promote the key. You have * to do that by hand with push(key, value). */ public final synchronized long getTime(T value) { Element e = valueToElement.remove(value); if(e == null) return -1; return e.time; } /** * Count the number of values after specified timestamp * @param timestamp * @return value count */ public synchronized int countValuesAfter(long t) { Set> s = elements.tailSet(new Element(t, null)); return s.size(); } /** * Remove all entries on or before the given time. */ public final synchronized void removeBefore(long t) { assert(elements.size() == valueToElement.size()); Set> s = elements.headSet(new Element(t, null)); for(Iterator> i = s.iterator();i.hasNext();) { Element e = i.next(); valueToElement.remove(e.value); i.remove(); } assert(elements.size() == valueToElement.size()); } // FIXME this is broken if timestamp != -1 public final synchronized Object[] pairsAfter(long timestamp, E[] valuesArray) { Set> s = elements.tailSet(new Element(timestamp, null)); Long[] timeArray = new Long[s.size()]; int i = 0; for (Element e : s) { timeArray[i] = e.time; valuesArray[i] = (E) e.value; i++; } return new Object[] { valuesArray, timeArray }; } }