Why does exist WeakHashMap, but absent WeakSet?
Asked Answered
S

2

83

From J. Bloch

A ... source of memory leaks is listeners ... The best way to ensure that callbacks are garbage collected promptly is to store only weak references to them, for instance, by storing them only as keys in a WeakHashMap.

So, why there isn't any WeakSet in the Java Collections framework?

Sticktight answered 31/10, 2010 at 11:35 Comment(3)
Stas, can you accept mart's upvoted, correct answer instead of Martin's downvoted, incorrect answer?Alexis
While Joshua Bloch wrote a lot of reasonable advice for Java programmers, this seems to be an awful exception. Storing listeners into a WeakHashMap does never “ensure that callbacks are garbage collected promptly”, but rather makes them horribly non-deterministic. The garbage collector will only run when there is insufficient memory, hence, such weak listeners may be dangling around an arbitrary long time and still getting executed, but even worse, such listeners might spuriously disappear when you still need them, as it now needs an actually unrelated strong reference to keep them alive.Waiver
You're allowed (even encouraged) to remove them explicitly, but using a weak collection ensures that they don't leak via this route.Alvinia
B
207

Collections.newSetFromMap

Set<Object> weakHashSet = 
    Collections.newSetFromMap(
        new WeakHashMap<Object, Boolean>()
    );

As seen in Collections.newSetFromMap documentation, passing a WeakHashMap to get a Set.

Broyles answered 31/10, 2010 at 11:47 Comment(15)
actually any Set in java collection contains Map for storing.Broyles
Yep, but why there is no specific class for such stuff?Sticktight
It's easy to imagine why the maintainers of java.util might have wanted to stop having to provide dual Map and Set versions of everything they do, and opted to just provide newSetFromMap() instead... isn't it?Acetometer
@Kevin Bourrillion : It is. But there is some strange about their selections.Sticktight
I think the javadoc might be wrong in this case. The created set will still store values as a value in the backing map (instead of as a key). WeakHashMap uses references on the keys, but the values are still strongly referenced. This will basically only store a weak reference to the hashcode of all the values added.Raddie
It's worth noting that Collections#newSetFromMap is missing from Android before API 9. It's not difficult to find an implementation to compile into your app, though, but it's a compatibility gotcha.Freudian
@Raddie The JavaDoc is correct. Note the code in this answer returns a Set of Objects not Booleans. newSetFromMap creates a set of the type of the keys, not the values.Subservient
@mart: Not true. BitSet doesn't.Toast
@RokKralj: BitSet is not a Collection.Moncrief
Tried this newSetFromMap solution; big limitation: can't retrieve items from set without iterating (performance killer). Want to use weak references to deduplicate objects, but can only compare keys and retrieve values, not retrieve previously stored key that matches (unless iterating). Set needs a key-getter to retrieve the actual object stored which matches with .equals().Metallurgy
@Metallurgy A WeakSet, if it existed in the standard library, would probably not be good in your use case because, as you mention, the Set interface does not support retrieving the actual element that is stored without iterating to find it. You might try ReferenceMap from Apache Commons Collections, created with WEAK key and value reference type, and store each element as the key and value of the ReferenceMap entry.Fabien
@DanielTrebbien Thanks. Ended up going with Guava: CacheBuilder.newBuilder().weakValues().build() which worked for my use case. Just a bit disappointed that this limitation exists in the Set interface. Would be nice to have a method to do storedKey = getKey(equivalentKeyForLocating). Could do so much with that.Metallurgy
The types can also be inferred, leaving out the Boolean type that is invisible once you've let go of the reference to the Map (as is recommended): Set<K> weakHashSet = Collections.newSetFromMap(new WeakHashMap<>());Flaherty
@KevinBourrillion As for your theory that the Collections Framework designers omitted certain Set implementations by way of Collections.newSetFromMap… apparently not. The Collections Framework arrived in Java 2, but newSetFromMap did not arrive until Java 6, eight years later.Cotopaxi
This refutes nothing I said; it remains very conceivable that multiple feature requests that had accumulated over 8 years were closed when that method was added.Acetometer
C
3

While you can indeed use Collections.newSetFromMap() to get a WeakSet, it's use cases are actually quite limited.

If you want to implement something like String.intern() you might want to have a look at Guava's Interners.newWeakInterner() functionality instead.

Copeck answered 2/3, 2018 at 14:0 Comment(1)
if someone is looking for optimizing memory use, the performance are quite better than guava weak interners. docs.geotools.org/stable/javadocs/org/geotools/util/….Pandorapandour

© 2022 - 2024 — McMap. All rights reserved.