-2

I have one query , I have developed a Map which I want to sort please advise..

  HashMap map=new HashMap();//HashMap key random order.
     map.put("Amit","Java");
     map.put("Saral","J2EE");
     map.put("ty","Spring");
     map.put("Anupam","Hibernate");
     map.put("Ravi",".Net");
     map.put("Saral","Andriod");//same key but different value 
     map.put("Nitin","PHP");
     map.put("hj","Spring1");
     System.out.println("There are "+map.size()+" elements in the map.");
     System.out.println("Content of Map are...");
     Set s=map.entrySet();
     Iterator itr=s.iterator();
     while(itr.hasNext())
     {
         Map.Entry m=(Map.Entry)itr.next();
         System.out.println(m.getKey()+"\t"+m.getValue()+"\t"+ m.hashCode());
      }
  • 1
    `HashMap` has no concept of ordering, so you will need a different data structure or will have to copy your data to something that has ordering. Now the question is whether or not you want always-ordered or ordering on demand. – wkl Apr 17 '12 at 16:42
  • didn't get that please show the demo, if possible then take the upper one code and on the same show the ordering that will be a great help..!!thanks in advance..!! – user1338319 Apr 17 '12 at 16:47
  • @user why don't you try it out for yourself? It might help to read some resources online. – simchona Apr 17 '12 at 16:49
  • My question is do you want your data to always be ordered? Dilum's answer of using a `TreeMap` would always keep your data ordered based on your map's keys (Which are `string`s). If you added a new entry into the `TreeMap`, the tree would maintain ordering correctly. "Ordering on demand" means that you don't care about how the data is ordered, and only sometimes will you actually care about ordering/sorting. – wkl Apr 17 '12 at 16:50

2 Answers2

4

If you want it to be sorted by (string) keys, then simply use java.util.TreeMap instead of a HashMap.

Dilum Ranatunga
  • 13,254
  • 3
  • 41
  • 52
0

There are some things to consider which will affect what the solution you want really is.

You are using a HashMap<K, V>, which implies you want the association and key lookup behavior of a Map-type.

HashMap itself does not care about ordering - whenever you insert a key-value pair, the key's hashCode() method is called and the map stores the pairing using that hash code. String objects already have a correct hashCode() implementation, so you can use them as keys as you do without problem.

Of course, when it comes to retrieving or iterating over your HashMap, you will get things in a pretty much random order because the hash map stores items by the hash code, and ordering can change every time you add/remove an object.

Since you want to "sort" your data, I assume you mean you want your pairings back, ordered by key (sorted by the names you put in).

Now here are where the considerations come in:

Keeping Data Always Sorted

If you want your data to always be sorted, meaning every time you insert a new key-value pair, that you want to keep the natural ordering of elements, then there are some convenient data structures in the Java API you can use:

Using TreeMap

As Dilum already mentioned, you can use java.util.TreeMap, which implements the java.util.SortedMap interface, which will guarantee an ordering to the keys in the map.

Example:

import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

public class SortedMapExample {
    public static void main(String[] args) {
        SortedMap<String, String> map = new TreeMap<String, String>();
        map.put("Amit","Java");
        map.put("Saral","J2EE");
        map.put("ty","Spring");
        map.put("Anupam","Hibernate");
        map.put("Ravi",".Net");
        map.put("Saral","Andriod");//same key but different value 
        map.put("Nitin","PHP");
        map.put("hj","Spring1");

        for (Map.Entry<String, String> e : map) {
            System.out.println(String.format("%s - %s", e.getKey(), e.getValue()));
        }
    }
}

You will get this back if you run this code:

λ > java SortedMapExample
Amit - Java
Anupam - Hibernate
Nitin - PHP
Ravi - .Net
Saral - Andriod
hj - Spring1
ty - Spring

Using ConcurrentSkipListMap

ConcurrentSkipListMap is a newer data structure, first available with Java 6. It also implements the SortedMap interface, with an underlying implementation involving skip lists.

It is also a concurrent collection, which you can read more about in this article from Brian Goetz. Java Concurrent Collections live in java.util.concurrent, and to be brief, are high performance thread-safe collections.

It's easy enough to alter the previous example to using a ConcurrentSkipListMap.

import java.util.Map;
import java.util.SortedMap;
import java.util.concurrent.ConcurrentSkipListMap; // this changed

public class SortedMapExample {
    public static void main(String[] args) {
        // the following line changed - now you know the power of programming to interfaces
        SortedMap<String, String> map = new ConcurrentSkipListMap<String, String>();
        map.put("Amit","Java");
        map.put("Saral","J2EE");
        map.put("ty","Spring");
        map.put("Anupam","Hibernate");
        map.put("Ravi",".Net");
        map.put("Saral","Andriod");//same key but different value 
        map.put("Nitin","PHP");
        map.put("hj","Spring1");

        for (Map.Entry<String, String> e : map) {
            System.out.println(String.format("%s - %s", e.getKey(), e.getValue()));
        }
    }
}

Run this code, you'll get the exact same output:

λ > java SortedMapExample
Amit - Java
Anupam - Hibernate
Nitin - PHP
Ravi - .Net
Saral - Andriod
hj - Spring1
ty - Spring

Sorting on Demand

Perhaps in your use case, you don't want to always keep your data sorted. Maybe it's a performance concern (please benchmark that if that is your concern), and you really only need to sort data when requested.

You can, in your case, use Collections.sort on the keys of your data.

Sticking with your HashMap example:

import java.util.Collections;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;

public class UnsortedExample {
   public static void main(String[] args) {
      Map<String, String> map = new HashMap<String, String>();
      map.put("Amit", "Java");
      map.put("Saral","J2EE");
      map.put("ty","Spring");
      map.put("Anupam","Hibernate");
      map.put("Ravi",".Net");
      map.put("Saral","Andriod");//same key but different value
      map.put("Nitin","PHP");
      map.put("hj","Spring1");

      List<String> keys = new ArrayList<String>(map.keySet());
      Collections.sort(keys);

      for (String key : keys) {
          System.out.println(String.format("%s - %s", key, map.get(key)));
      }
   }
}

Here I pulled the keySet() from the map, which returns a Set object containing all the keys in the map. I then turned that into an ArrayList and sorted the list, then iterated over the list and pulled the value out of the map.

You can also use java.util.TreeSet when you pull out the keySet(), and TreeSet is naturally ordered just like TreeMap and ConcurrentSkipListMap.

Example of using a SortedSet:

java.util.SortedSet<String> keys = new java.util.TreeSet<String>(map.keySet());

As an aside, there is a java.util.concurrent.ConcurrentHashMap class as well, which is the Java Concurrency version of java.util.HashMap and works very well in multi-threaded environments where concurrent access will occur.

Whoa whoa, why is the ordering weird?

This example is using English (I don't know much about 'sorted' non-English languages), but you can see that there is some strangeness in the ordering.

For example, in the output in the previous examples, "hj" came after "Saral", whereas a human would probably order the list truly alphabetically, such that "hj" came between "Anupam" and "Nitin".

By default, java.lang.String implements the Comparable interface, defining a natural ordering. Java, like pretty much every other programming language in use, orders capital English letters before lowercase letters.

The simple explanation is that in ASCII/UTF-8, English capital letters come before English lowercase letters.

However, you might want a different alphabetical sort which ignores case. For that you will have to write your own class that implements Comparator, and pass it to the constructor of TreeMap or ConcurrentSkipListMap, or to the Collections.sort method that takes a comparator object.

I'll leave that as an exercise to you if you want to achieve such an ordering.

Conclusion

Your question, on the surface, is just about sorting the data in your Map-like object.

You have the option to use:

  • java.util.TreeMap - keeps ordering of elements by natural order
  • java.util.concurrent.ConcurrentSkipListMap - same as above, but comes with good thread-safety
  • Sort on demand using Collections.sort()

I don't know what your use for this data structure or what environment it is being used in. For example, if there is multithreaded access to your HashMap, then you need synchronization mechanisms which HashMap by itself doesn't have. For that, it's easier to use java.util.concurrent.ConcurrentSkipListMap or java.util.concurrent.ConcurrentHashmap.

So depending on how you are using your data, there are different ways to have your data sorted. If you want always-sorted, then I'd recommend using ConcurrentSkipListMap (for multithreaded) or TreeMap. If you need the insertion performance of HashMap, which is amortized O(1), then you probably need to be using HashMap and sorting the "on demand" way, or keeping a separate data structure to order your keys. Do profiling if you have performance concerns for your specific use.

And some additional details:

  • I am using for each loops to traverse my data structures above. If you only need element access (and not mutation/deletion), for-each is very nice syntactic sugar over explicit iterators.
  • In my examples I use the interface as my variable's type, rather than the concrete class (in your code, you have HashMap<...> map = new HashMap<...>();. Generally it's a good idea to program to interfaces unless you have very exact requirements that necessitate using something specifically.
Community
  • 1
  • 1
wkl
  • 77,184
  • 16
  • 165
  • 176