3

In Java, I often need to lazily get an entry of a ConcurrentMap, creating only if necessary.

For example I may have

ConcurrentMap<String, AtomicReference<Something>> a = new ConcurrentHashMap<>();
ConcurrentMap<String, Something> b = new ConcurrentHashMap<>();

I wanted to make a generic function to do this job so that I don't repeat myself the rather cumbersome double checking code for every type.

the following was as far as I can get:

<K, V, C extends V> V ensureEntry(ConcurrentMap<K, V> map, K key, Class<? super C> clazz) throws Exception {
    V result = map.get(key);
    if (result == null) {
        final V value = (V)clazz.newInstance();
        result = map.putIfAbsent(key, value);
        if (result == null) {
            result = value;
        }
    }
    return result;
}

And then I can use it like:

AtomicReference<Something> ref = ensureElement(a, "key", AtomicReference.class);
Something something = ensureElement(b, "another key", Something.class);

The question is that: the function is quite dirty and still has an unsafe generic class cast (the (V)). Would a completely generic and cleaner one possible? maybe in Scala?

Thanks!

Community
  • 1
  • 1
lyomi
  • 4,230
  • 6
  • 30
  • 39
  • 1
    Why would you have `Class super C>` instead of `Class extends V>`? If you had the meaningful upper bound, the downcast would not be needed and the whole thing would be typesafe. – Marko Topolnik Sep 02 '13 at 07:58
  • 3
    You should also consider plain old `synchronized`, which would eliminate the boilerplate double-check you need for the lock-free approach. These days, lock-free computing is more a buzzword than a useful technique. It has its legitimate applications, but it is definitely overused today. – Marko Topolnik Sep 02 '13 at 08:02
  • It doesn't compile for generic types. In the above example the type AtomicReference doesn't extend AtomicReference. Similar errors for super V>. – lyomi Sep 02 '13 at 08:03
  • But `Class super C>` is still meaningless because you use `Class` as a *producer*. If you can't define an upper bound, then you should just use `Class>` with the same effect. – Marko Topolnik Sep 02 '13 at 08:07
  • That makes sense. I should really use a factory but only when Java 8 comes out.. Thanks! – lyomi Sep 02 '13 at 08:25
  • 1
    May be you could use the [guava caches](https://code.google.com/p/guava-libraries/wiki/CachesExplained) ? – gontard Sep 02 '13 at 10:35

1 Answers1

2

With Java 8 lambda, the following is the simplest I could get..

<K, V> V ensureEntry(ConcurrentMap<K, V> map, K key, Supplier<V> factory) {
    V result = map.get(key);
    if (result == null) {
        V value = factory.get();
        result = map.putIfAbsent(key, value);
        if (result == null) {
            result = value;
        }
    }
    return result;
}

ConcurrentMap<String, AtomicReference<Object>> map = new ConcurrentHashMap<>();
ensureEntry(map, "key", () -> new AtomicReference<>());
// or
ensureEntry(map, "key", AtomicReference::new);
lyomi
  • 4,230
  • 6
  • 30
  • 39