Search code examples
javaconcurrencydictionarygarbage

Map allowing to putIfAbsent without creating the value when key already exists


I'd like to use a map that would be equivalent to ConcurrentMap (I want the equivalent of the putIfAbsent method) but that would not force me to create the object beforehand.

For example when I do this:

m.putIfAbsent( key, new CyclingArray() );

I may end up creating a new CyclingArray (whatever that is) object for nothing.

Of course I realize I could lock up the entire map but that would be defeating the whole point of a ConcurrentMap.

Could something like the following work conceptually?

   m.putIfAbsent( key, new Callback<CyclingArray>() {
     @Override
     public CyclingArray provide() {
       return new CyclingArray();  // only called if the key wasn't already present
     }         
   }

Do you know of any library offering a map that:

  1. offer an "interface" like the one offered by ConcurrentMap, including a putIfAbsent method.
  2. does only lock on the segment we're going to work with (like the ConcurrentHashMap implementation does, for example)
  3. allows to optionally create the value, if and only if the key wasn't already present and hence dodge useless garbage generation.
  4. does not force me to use first a containsKey and then a putIfAbsent because this, also, somehow defeats the purpose of putIfAbsent.

Note that I'm not asking if the above example can be done with a ConcurrentMap (it cannot AFAIK).

I was thinking about extending ConcurrentHashMap and overloading putIfAbsent with the callback version but sadly ConcurrentHashMap internally uses a final Segment class.

Before re-inventing the wheel I'd like to know if there are any maps out there already offering a similar functionality.


Solution

  • This is a common use case you are looking for, its called memoization. I would look at MapMaker

    You would be able to create a computingMap and put your creating function there:

     ConcurrentMap<Key, CyclingArray> graphs = new MapMaker()
           .concurrencyLevel(32)
           .makeComputingMap(
               new Function<Key, CyclingArray>() {
                    public CyclingArray  apply(Key key) {
                        return new CyclingArray();  // only called if the key wasn't already    
                    }              
               });
    

    Here the Function will only be called if the Key is not present

    And I do know future plans in Java have a computingMap type interface will come with standard Java, unfortunately at this point you will have to delegate to google-collections.