I wanted a concurrent set with weak references to elements. I thought of doing this using Guava's MapMaker
:
Set<Object> concurrentSet = Collections.newSetFromMap(
new MapMaker<Object, Boolean>().weakKeys().makeMap());
Guava will automatically give you identity hashes with weak keys.
However, it turns out that MapMaker
does not allow type parameters.
file.java:123 type com.google.common.collect.MapMaker does not take parameters
new MapMaker<Object, Boolean>().weakKeys().makeMap());
^
Any solutions how I can obtain a concurrent set with weak references to elements?
To flesh out the code being hinted at in the accepted answer and comments, here is my own take on the code to get a concurrent weak set.
Using the idiom suggested in the class documentation, use Collections.newSetFromMap to wrap a concurrent Map as a Set. The use of Boolean
as the map's Value type is just filler, inconsequential.
Set< YourObjectTypeGoesHere > concurrentWeakSet =
Collections.synchronizedSet(
Collections.newSetFromMap(
new WeakHashMap< YourObjectTypeGoesHere , Boolean >()
)
)
;
The parameterized types can be inferred, simplifying the code to:
Set< YourObjectTypeGoesHere > concurrentWeakSet =
Collections.synchronizedSet(
Collections.newSetFromMap(
new WeakHashMap<>() // Types inferred, so omitted.
)
)
;
Alternatively, we can use MapMaker
from the Google Guava library.
Instead of using that doc’s example wrapping a WeakHashMap, we substitute the WeakHashMap with a map from Google Guava’s MapMaker per the Guava doc’s suggestion to call new MapMaker().weakKeys().makeMap()
. The Guava doc notes that this map "compares keys using object identity whereas WeakHashMap uses Object.equals
".
Using it is easier than understanding it!
To create the concurrent weak set, copy paste the following code. Replace two occurrences of YourObjectTypeGoesHere
.
int level = 16; // Basically, the approximate number of threads that may simultaneously try to mutate the map. See Guava doc.
ConcurrentMap<YourObjectTypeGoesHere , Boolean> concurrentWeakMap = new MapMaker().concurrencyLevel( level ).weakKeys().makeMap(); // Google Guava team recommends MapMaker > weakKeys > makeMap as a replacement for weakHashMap.
Set<YourObjectTypeGoesHere> concurrentWeakSet = Collections.newSetFromMap( concurrentWeakMap ); // Concurrency protection carries over to Set wrapper.
To add to the set:
concurrentWeakSet.add( myObject );
To access the elements in set:
Iterator<YourObjectTypeGoesHere> iterator = concurrentWeakSet.iterator();
while(iterator.hasNext()) {
YourObjectTypeGoesHere myObject = iterator.next();
if( myObject != null ) { // Not sure if needed. Is it possible for object to be garbage-collected but not yet have its entry removed from the Set/Map?
// Work with the object.
}
}
Being weak means there is no need to remove elements. As the elements become disused and garbage-collected, they disappear from our collection (Map/Set).
As explained in the documentation, MapMaker
is not a generic type; it's <Object, Object>
. This means you can put anything as key or value, you just need to cast it when retreiving. Quoting the link:
ConcurrentMap<Request, Stopwatch> timers = new MapMaker()
.concurrencyLevel(4)
.weakKeys()
.makeMap();
To get a Set
with the Map
entries, you just call Map#entrySet()
.
Your orriginal approach will work fine, you just have to put your type parameters on the call to makeMap()
Set<Object> concurrentSet = Collections.newSetFromMap(
new MapMaker().weakKeys().<Object, Boolean> makeMap());
using Java's lesser used generic method invocation syntax. Java Specification.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With