sclass WeakValueMap is AutoCloseable, IntSize { // A value that has been stored with this key has been garbage // collected. event valueForKeyReleased(A key); // internal Map> map = syncMap(); RunnablesReferenceQueue queue; bool myQueue = true; *() {} *(RunnablesReferenceQueue *queue) { myQueue = false; } class MyRef extends WeakRef is Runnable { A key; *(A *key, B value) { super(value, queue()!); } run { bool current = syncMapRemoveKeyAndValuePair(map, key, this); // Note that the value may be stale (replaed by another value in // the meantime). If you want to distinguish that kind of event // from a non-stale value being released, check the variable // "current". valueForKeyReleased(key); } } // public methods follow B get(A key) { ret getWeakRef(map.get(key)); } B put(A key, B value) { synchronized(mutex()) { if (value == null) { B old = getWeakRef(map.get(key)); map.remove(key); ret old; } else ret getWeakRef(map.put(key, new MyRef(key, value))); } } O mutex() { ret collectionMutex(map); } RunnablesReferenceQueue queue() { synchronized(mutex()) { if (queue == null) queue = new RunnablesReferenceQueue; ret queue; } } close { if (myQueue) synchronized(mutex()) { dispose queue; } } public int size() { ret map.size(); } Map snapshot() { synchronized(mutex()) { new Map snapshot; for (key, ref : map) mapPut(snapshot, key, getWeakRef(ref)); ret snapshot; } } void setQueue aka queue(RunnablesReferenceQueue queue) { synchronized(mutex()) { if (queue == null || this.queue == queue) ret; if (this.queue != null) fail("Can't change queue once we're started"); this.queue = queue; myQueue = false; } } }