Puru--
Puru--

Reputation: 1111

ConcurrentHashMap dilemma in Java

CocncurrentHashMap provides a method to atomically check and add an element if it is not present via putIfAbsent method as shown in the example below

xmlObject = new XMLObejct(xmlId);
mapOfXMLs.putIfAbsent(xmlId, xmlObject);

However my dilemma is that , I have to create that xmlObject in advance. Is there a way to postpone the object creation after the key present check.

I want all three things below to happen atomically

  1. Check if the key present
  2. Create object if key is not present.
  3. Add the object to map.

I know I can achieve this using synchronized block , If I am using a synchronized block , why use a CocurrentHashMap?

Upvotes: 2

Views: 134

Answers (3)

forty-two
forty-two

Reputation: 12817

I've encountered this scenario a couple of times, and they allowed for the value to be created lazily. It may not apply to your use case, but if it does, this is basically what I did:

static abstract class Lazy<T> {

    private volatile T value;

    protected abstract T initialValue();

    public T get() {
        T tmp = value;
        if (tmp == null) {
            synchronized (this) {
                tmp = value;
                if (tmp == null)
                    value = tmp = initialValue();
            }
        }
        return tmp;
    }
}

static ConcurrentHashMap<Integer, Lazy<XmlObject>> map = new ConcurrentHashMap<>();

and then populating the map:

    final int id = 1;

    map.putIfAbsent(id, new Lazy<XmlObject>() {
        @Override
        protected XmlObject initialValue() {
            return new XmlObject(id);
        }
    });

    System.out.println(map.get(id).get());

You can of course create a specialized LazyXmlObject for convenience:

static class LazyXmlObject extends Lazy<XmlObject> {
    private final int id;

    public LazyXmlObject(int id) {
        super();
        this.id = id;
    }

    @Override
    protected XmlObject initialValue() {
        return new XmlObject(id);
    }
}

and the usage would be:

    final int id = 1;

    map.putIfAbsent(id, new LazyXmlObject(id));

    System.out.println(map.get(id).get());

Upvotes: 0

djechlin
djechlin

Reputation: 60748

The standard, almost perfect pattern is this:

Foo foo = map.get(key);
if(foo == null) {
    map.putIfAbsent(new Foo());
    foo = map.get(key);
}

It does sometimes result in an extra object, but extremely infrequently, so from a performance standpoint is certainly fine. It only wouldn't be fine if constructing your object inserted into a database or charged a user or some such.

Upvotes: 2

Marco13
Marco13

Reputation: 54611

The Guava Caches offer such a functionality ( http://code.google.com/p/guava-libraries/wiki/CachesExplained ) though it's somewhat hidden.

If you can already use Java 8, then you can use computeIfAbsent. But I guess if you could use it, you would not have asked....

Upvotes: 3

Related Questions