-1

I am new to java. I want to sort a concurrent hash map by value. I found a way of doing it here - sort concurrent map entries by value

Is there a simpler way of doing it? Can someone please explain it with an example ?

Thanks.

Community
  • 1
  • 1
user379151
  • 1,289
  • 1
  • 16
  • 25
  • 1
    Hash maps don't particularly have an order. What is your end goal? – Corbin May 14 '12 at 22:58
  • This question might help: (Sorting map by value) http://stackoverflow.com/questions/2889443/how-to-sort-a-mapkey-value-on-the-values-in-java-with-google-collections-orde – andersoj May 14 '12 at 23:06
  • Not without breaking concurrency, probably. Copy it to another map and use the normal approaches for sorting that map by value. – Louis Wasserman May 15 '12 at 06:03
  • You could apply [this idea (95 points answer)](http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java) to the `ConcurrentSkipListMap` proposed by @Gray – assylias May 15 '12 at 09:15

3 Answers3

4

Another solution would be to switch to using the ConcurrentSkipListMap which was added in Java 6. To quote from the Javadocs:

This class implements a concurrent variant of SkipLists providing expected average log(n) time cost for the containsKey, get, put and remove operations and their variants. Insertion, removal, update, and access operations safely execute concurrently by multiple threads. Iterators are weakly consistent, returning elements reflecting the state of the map at some point at or since the creation of the iterator. They do not throw ConcurrentModificationException, and may proceed concurrently with other operations. Ascending key ordered views and their iterators are faster than descending ones.

SkipLists are probabilistic replacements for balanced trees. They have the same O as trees but usually their implementation is markedly simpler. If most of your operations are hash-table lookups (O(1) by definition) then you will see a performance different for decent table sizes, but if you require sorting often, this may be a better solution.

I just wish Java provided a non-concurrent version of this great data structure.

Gray
  • 115,027
  • 24
  • 293
  • 354
  • 1
    In what possible way would a non-concurrent version of `ConcurrentSkipListMap` be preferable to a `TreeMap`? – Louis Wasserman May 15 '12 at 06:02
  • Speed. Skip lists area typically faster than other tree implementations. – Gray May 15 '12 at 11:24
  • I would honestly be pretty shocked if the JDK authors hadn't benchmarked it, but I would suspect trees are also somewhat better at supporting the "generalist" features of `NavigableMap`. – Louis Wasserman May 15 '12 at 13:34
2

Depending on your use case I would create another class representing your data structure that is composed of a hashmap and a list say for maintaining ordering of values.

More details about this here:
Sort a Map<Key, Value> by values (Java)
Sorting HashMap by values
http://www.coderanch.com/t/382750/java/java/Sorting-HashMap-values

You can also extend ConcurrentHashMap to override entrySet and keySet methods to return entries/keys in the order of your values.

public class OrderedValueHashMap<K,V extends Comparable<V>> extends ConcurrentHashMap<K, V> {

@Override
public Set<Map.Entry<K, V>> entrySet() {
    Set<Map.Entry<K, V>> orderedValueEntrySet = new TreeSet<Map.Entry<K,V>>(new Comparator<Map.Entry<K,V>>() {

        @Override
        public int compare(java.util.Map.Entry<K, V> o1,
                java.util.Map.Entry<K, V> o2) {
            return o1.getValue().compareTo(o2.getValue());
        }
    });
    orderedValueEntrySet.addAll(super.entrySet());
    return orderedValueEntrySet;
}

@Override
public Set<K> keySet() {
    Set<K> orderedKeySet = new LinkedHashSet<K>();
    for(Map.Entry<K, V> e : entrySet()) {
        orderedKeySet.add(e.getKey());
    }
    return orderedKeySet;
}

}

The above solution is not optimal if you are calling the methods keySet/entrySet very often as it sorts entries on each call. You may want to cache these results to avoid re-computation while the internal state of the map has not changed.

An example run of the above is as follows:

public static void main(String[] args) {
    ConcurrentHashMap<String,Integer> map = new OrderedValueHashMap<String,Integer>();
    map.put("a", 3);
    map.put("b", 7);
    map.put("c", 1);
    map.put("q", 2);

    for (Map.Entry<String, Integer> entry : map.entrySet()) {
        System.out.println(entry);
    }

    for (String key : map.keySet()) {
        System.out.println(key);
    }

}

Output:

c=1
q=2
a=3
b=7
c
q
a
b
Community
  • 1
  • 1
Leon
  • 520
  • 3
  • 10
1

Is there a simpler way of doing it?

As far as I know, No.

Certainly, there is no way to do an in-place sort of a concurrent hash map. A CHM is inherently unordered so you have to put the entries into a different data structure to represent the orderedness of the entries.

If you told us what your requirements were, perhaps we could suggest an alternative strategy.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • Thanks for replying. The concurrent hash map is of the form - ConcurrentMap stringFrequencyMap = new ConcurrentHashMap; I need to be able to sort based on the value (i.e. frequencies) – user379151 May 14 '12 at 23:03
  • @user379151 - insufficient detail. Is this a once off activity? Does the table need to stay sorted? Is the operation allowed to block concurrent updates? And so on. – Stephen C May 15 '12 at 06:41
  • @user379151 - for instance, if this is a once-off and the table doesn't need to "stay sorted" then just copy the map's entry set to a List or array, and sort it. – Stephen C May 15 '12 at 06:44