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; // new RestartableCountdown countdown; // TODO *() {} *(long *standardExpiryTime) {} *(long *standardExpiryTime, O *onChange) {} bool clean() { bool changes = false; Pair p; while ((p = queue.peek()) != null && sysTime() >= p.a) { 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); changes = true; change(); } } ret changes; } void change() { callF(onChange); } 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)); if (p == null || renewOnOverwrite) queue.add(Pair(timeout, a)); change(); ret pairB(p); } 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; } 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); } public Set> entrySet() { clean(); // TODO: mutex ret mapValues(f pairB, byKey).entrySet(); } public Set keySet() { clean(); ret byKey.keySet(); } public int size() { clean(); ret byKey.size(); } void setStandardExpiryTime(long ms) { standardExpiryTime = ms; } ExpiringMap2 setMap(Map innerMap) { byKey = innerMap; this; } }