sclass ExpiringMap2 extends AbstractMap { Map> byKey = new HashMap; new PriorityBlockingQueue> queue; long standardExpiryTime; // ms bool renewOnOverwrite = true; O onChange; // new RestartableCountdown countdown; // TODO *() {} *(long *standardExpiryTime) {} *(long *standardExpiryTime, O *onChange) {} void clean() { 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); change(); } } } 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(PairComparedByA(p.a, a)); byKey.put(a, pair(timeout, b)); change(); if (p == null || renewOnOverwrite) queue.add(PairComparedByA(timeout, a)); ret pairB(p); } public B remove(O a) { clean(); Pair p = byKey.get(a); if (p == null) null; queue.remove(PairComparedByA(p.a, a)); byKey.remove(a); change(); ret p.b; } public B get(O a) { clean(); ret pairB(byKey.get(a)); } 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(); } }