Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Atomically ensuring a ConcurrentMap entry

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!

like image 708
lyomi Avatar asked Apr 19 '26 12:04

lyomi


1 Answers

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);
like image 170
lyomi Avatar answered Apr 21 '26 02:04

lyomi