package freenet.support;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
/**
* Variant on LRUMap which provides an efficient how-many-since-time-T operation.
*/
public class TimeSortedHashtable<T extends Comparable<T>> {
public TimeSortedHashtable() {
this.elements = new TreeSet<Element<T>>();
this.valueToElement = new HashMap<T, Element<T>>();
}
private static class Element<T extends Comparable<T>> implements Comparable<Element<T>> {
Element(long t, T v) {
time = t;
value = v;
}
long time;
final T value;
@Override
public int compareTo(Element<T> 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<Element<T>> elements;
private final HashMap<T, Element<T>> 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<T> e = valueToElement.get(value);
if(e == null) {
e = new Element<T>(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<T> 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<T> 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<Element<T>> s = elements.tailSet(new Element<T>(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<Element<T>> s = elements.headSet(new Element<T>(t, null));
for(Iterator<Element<T>> i = s.iterator();i.hasNext();) {
Element<T> 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, T[] valuesArray) {
Set<Element<T>> s = elements.tailSet(new Element<T>(timestamp, null));
Long[] timeArray = new Long[s.size()];
int i = 0;
for (Element<T> e : s) {
timeArray[i] = e.time;
valuesArray[i] = e.value;
i++;
}
return new Object[] { valuesArray, timeArray };
}
}