GingerHead
GingerHead

Reputation: 8230

How to sort a map

I have a Map to sort as follows:

Map<String, String> map = new HashMap(); 

It contains the following String keys:

String key = "key1.key2.key3.key4" 

It contains the following String values:

String value = "value1.value2"

where the key and value can vary by their number of dot sections from key1/value1 to key1.key2.key3.key4.key5/value1.value2.value3.value4.value5 non-homogeneously

I need to compare them according to the number of dots present in keys or in values according to the calling method type key / value :

sortMap(Map map, int byKey);

or

sortMap(Map map, int byValue);

The methods of course will return a sorted map.

Any help would be appreciated.

Upvotes: 1

Views: 643

Answers (4)

GingerHead
GingerHead

Reputation: 8230

I did it by the following:

@SuppressWarnings({ "unchecked", "rawtypes" })
public static Map sortMap(Map unsortedMap) {

    List list = new LinkedList(unsortedMap.entrySet());
    // sort list based on comparator
    Collections.sort(list, new Comparator() {
        public int compare(Object o1, Object o2) {
            String value1 = (String)((Map.Entry) (o1)).getValue();
            String value2 = (String)((Map.Entry) (o2)).getValue();
            // declare the count
            int count1 = findOccurances(value1, '.');
            int count2 = findOccurances(value2, '.');
            // Go to thru the comparing
            if(count1 > count2){
                return -1;
            }
            if(count1 < count2){
                return 1;
            }
            return 0;
        }
    });

    // put the sorted list into map again
    Map sortedMap = new LinkedHashMap();
    for (Iterator it = list.iterator(); it.hasNext();) {
        Map.Entry entry = (Map.Entry) it.next();
        sortedMap.put(entry.getKey(), entry.getValue());
    }
    return sortedMap;
}

With the following helper method:

private static int findOccurances(String s, char chr) {
    final char[] chars = s.toCharArray();
    int count = 0;
    for (int i = 0; i < chars.length; i++) {
        if (chars[i] == chr) {
            count++;
        }
    }
    return count;
}

Here, I can put some switch on the comparing part with an additional int argument to change between asc/desc.

I can change between values and keys through a switch of another int argument value to get my answer.

Upvotes: 0

Louis Wasserman
Louis Wasserman

Reputation: 198014

There is no way to impose any sort of order on HashMap.

If you want to order elements by some comparison on the keys, then use a TreeMap with some Comparator on the keys, or just use their default Comparable ordering.

If you want to order by the values, the only real option is to use a LinkedHashMap, which preserves the order that entries were put into the map, and then to sort the entries before inserting them into the map, or perhaps some non-JDK Map implementation. There are dirty hacks that make a key comparator that actually secretly compares the values, but these are dangerous and frequently lead to unpredictable behavior.

Upvotes: 4

Hans Cappelle
Hans Cappelle

Reputation: 17495

You should use a TreeMap and implement a ValueComparator or make the key and value objects that implement Comparable.

Must be a duplicate here...

edit: duplicate of (to name just one) Sort a Map<Key, Value> by values (Java)

Upvotes: 1

Andrzej Doyle
Andrzej Doyle

Reputation: 103777

For starters, you will need to be using an instance of SortedMap. If the map doesn't implement that interface, then it has an undefined/arbitrary iteration order and you can't control it. (Generally this is the case, since a map is a way of associating values with keys; ordering is an auxiliary concern.)

So I'll assume you're using TreeMap, which is the canonical sorted map implementation. This sorts its keys according to a Comparator which you can supply in the constructor. So if you can write such a comparator that determines which is the "lower" of two arbitrary keys (spoiler alert: you can), this will be straightforward to implement.

This will, however, only work when sorting by key. I don't know if it makes much sense to sort a map by value, and I'm not aware of any straightforward way to do this. The best I can think of is to write a Comparator<Map.Entry> that sorts on values, call Map.getEntrySet and push all the entries into a list, then call Collections.sort on the list. It's not very elegant or efficient but it should get the job done if performance isn't your primary concern.

(Note also that if your keys aren't immutable, you will run into a lot of trouble, as they won't be resorted when externally changed.

Upvotes: 1

Related Questions