Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concurrent set with weak references and identity hash

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?

like image 340
r.v Avatar asked Apr 12 '13 18:04

r.v


3 Answers

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.

Java, without Guava

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.
        )
    )
;

Guava

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".

Usage

Using it is easier than understanding it!

To Instantiate

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 add to the set:

concurrentWeakSet.add( myObject ); 

To Iterate

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.
    }
}

To Remove

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).

like image 193
Basil Bourque Avatar answered Nov 01 '22 08:11

Basil Bourque


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().

like image 36
m0skit0 Avatar answered Nov 01 '22 09:11

m0skit0


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.

like image 29
Stuart Avatar answered Nov 01 '22 09:11

Stuart