sclass ExpiringMap2 extends AbstractMap {
Map> byKey = new HashMap; // key -> pair(expiry sys time, value)
new PriorityBlockingQueue> queue; // queue(pair(expiry sys time, key))
long standardExpiryTime; // ms
bool renewOnOverwrite = true, renewOnGet;
O onChange;
bool useCountdown = true;
new RestartableCountdown countdown;
*() {}
*(long *standardExpiryTime) {}
*(long *standardExpiryTime, O *onChange) {}
*(double standardExpirySeconds) { standardExpiryTime = toMS(standardExpirySeconds); }
synchronized bool clean() {
bool changes;
Pair p;
long time = sysTime();
while ((p = queue.peek()) != null && time >= p.a) {
if (useCountdown) countdown.stop();
p = queue.poll();
ifdef ExpiringMap2_debug
print("ExpiringMap: retiring key " + p.b);
endifdef
Pair v = byKey.get(p.b);
if (v != null /*&& v.a == p.a*/) {
ifdef ExpiringMap2_debug
print("ExpiringMap: retired key " + p.b);
endifdef
byKey.remove(p.b);
set changes;
change();
}
}
if (useCountdown)
countdown.setTargetTime(p != null ? p.a : 0, r clean);
ret changes;
}
void change() { callF(onChange); }
synchronized public B put(A a, B b) {
clean();
long timeout = sysTime()+standardExpiryTime;
Pair p = byKey.get(a);
if (p != null && renewOnOverwrite)
queue.remove(Pair(p.a, a));
byKey.put(a, pair(timeout, b));
change();
if (p == null || renewOnOverwrite)
queue.add(Pair(timeout, a));
ret pairB(p);
}
synchronized public B remove(O a) {
clean();
Pair p = byKey.get(a);
if (p == null) null;
queue.remove(Pair(p.a, a));
byKey.remove(a);
change();
ret p.b;
}
synchronized public B get(O a) {
clean();
Pair p = byKey.get(a);
if (renewOnGet && p != null) {
queue.remove(Pair(p.a, a));
long timeout = sysTime()+standardExpiryTime;
byKey.put((A) a, pair(timeout, p.b));
queue.add(Pair(timeout, a));
}
ret pairB(p);
}
synchronized public Set> entrySet() {
clean();
// TODO: mutex
ret synchronizedSet(mapValues pairB(byKey).entrySet());
}
synchronized public Set keySet() {
clean();
ret synchronizedSet(byKey.keySet());
}
synchronized public int size() {
clean();
ret byKey.size();
}
void setStandardExpiryTime(long ms) { standardExpiryTime = ms; }
synchronized ExpiringMap2 setMap(Map innerMap) {
byKey = innerMap;
this;
}
}