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.