marcorossi
marcorossi

Reputation: 2011

Java ConcurrentSkipListMap: adding atomically another Collection object

I have a concurrent scenario where I have to write a lot to a sorted datastructure.

I thought about using ConcurrentSkipListMap for this reason. My definition is something like this: ConcurrentSkipListMap<K, List<V>>, which of course makes it quite difficult to manage insertions of the List<V> when the first element is inserted.

I.e.:

List<V> list = map.get(k);
if (list == null) {
    list = new LinkedList<V>();
    map.put(list);
}
list.add(v);

Of course this is not atomic. Using the class putIfAbsent() method would make it quite awkward and inefficient:

List<V> newElement = new LinkedList<V>();
List<V> previous = map.putIfAbsent(k, newElement);
if (previous != null) {
    previous.add(v);
} else {
    newElement.add(v);
}

One way is of course to create my own lock and protect a normal TreeMap, but as I have a real high write rate on this object, I'd prefer something designed specifically for it. Something like collections.defaultdict of python would be perfect, of course.

Upvotes: 1

Views: 671

Answers (1)

John Vint
John Vint

Reputation: 40266

A couple things.

First: The most effecient way to handle your the put-if-absent case is to do a pseudo double check

public void add(Object key, Object val) {
    List list = map.get(key);
    if (list == null) {
        list = new LinkedList();
        List temp = map.putIfAbsent(list);
        if (temp != null)
            list = temp;
    }
    list.add(val);
}

That is as effecient as you can get for the put-if-absent case.

Second: You still have a concurrency issue here with adding to the list. You may want to wrap the LinkedList in Collections.synchronizedList() before putting to the map.

public void add(Object key, Object val) {
        List list = map.get(key);
        if (list == null) {
            list = Collections.synchronizedList(new LinkedList());
            List temp = map.putIfAbsent(list);
            if (temp != null)
                list = temp;
        }
        list.add(val);
    }

Upvotes: 1

Related Questions