Home > Mobile >  ConcurrentHashMap computeIfAbsent tell if first time or not
ConcurrentHashMap computeIfAbsent tell if first time or not

Time:12-26

It's complicated for me to articulate a proper title for this. But an example should make it far simpler. Suppose I have this:

final class Cache {
   private static final ConcurrentHashMap<String, List<String>> CACHE = ...

   static String byName(String name) {
      return CACHE.computeIfAbsent(name, x -> // some expensive operation)
   }

}

The idea is probably trivial, this acts as a LoadingCache, much like guava or caffeine (in reality it is more complicated, but that is irrelevant to the question).

I would like to be able to tell if this was the first load into the CACHE, or it was a read of an existing mapping. Currently, I do this:

final class Cache {
   private static final ConcurrentHashMap<String, List<String>> CACHE = ...

   static String byName(String name) {
      boolean b[] = new boolean[1];
      List<String> result = CACHE.computeIfAbsent(name, x -> {
            b[0] = true;
            // some expensive operation)
      });

      if(b[0]) {
         // first load into the cache, do X
      } else {
         // do Y
      }

      return result;
   }

}

This works, but I am afraid I am missing something that ConcurrentHashMap can offer for me that would allow me to do the same. Thank you.

CodePudding user response:

I can think of a couple of ways to do this using the ConcurrentHashMap API. Both reply on using a mapping function that has side-effects. The idea is that the function makes a record of whether it was called, or what arguments it was called with.

The spec for computeIfAbsent says that the mapping function is only called if the key is absent. Alternatively, the spec for compute says that the mapping function is called with a null argument if the key is argument. In either case, if you record what happened in the mapper function via a side-effect on (say) a field of the mapper function/object, you can determine if the cache entry was already present or not.

To make this thread-safe, you need to create a fresh (thread-confined) instance of the mapper function.

CodePudding user response:

It seems the information if the provided mappingFunction has been already been called is not returned (local variable added). However you can call mappingCount() before and after calling computeIfAbsent. If these two count values do not differ then the mapping function has not been called. In this approach however you need to address the synchronisation on your own.

  • Related