Sorting the map based on keys
Basically it is not a HashMap
key-based sorting . For this I could use directly TreeMap
without winking :)
I currently have
Map<String, Object> favoritesMap = new HashMap<String, Object>();
and its contents can be
["Wednesdays" : "abcd"]
["Mondays" : "1234"]
["Not Categorized" : "pqrs"]
["Tuesdays" : "5678"]
I want to sort a HashMap based on keys and additionally I need "Not Categorized" to be the last one.
It is expected that when iterating over keySet it will be
["Mondays", "Tuesdays", "Wednesdays", "Not Categorized"] i.e. sorted on keys and "Not Categorized" is the last one
We thought about what you are going HashMap
to add when creating and at the end ["Not Categorized" : "pqrs"]
, but HashMap
does not guarantee the order :)
Any other pointers for a solution?
source to share
Are you specifically excluding TreeMap
for some external reason? If not, you can use TreeMap
with a custom made one Comparator
.
Have you considered any of the other SortedMap
s?
If it TreeMap
definitely does, I would expand HashMap
and make it look like there is another entry, but this is certainly not a trivial job. You must have a very good reason not to use SortedMap
before going down this road.
Added
Here's an example of how you can make a specific entry always sort to the end with TreeMap
:
// This key should always appear at the end of the list.
public static final String AtEnd = "Always at the end";
// A sample map.
SortedMap<String, String> myMap =
new TreeMap<>(
new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.equals(AtEnd) ? 1 : o2.equals(AtEnd) ? -1 : o1.compareTo(o2);
}
});
private void test() {
myMap.put("Monday", "abc");
myMap.put("Tuesday", "def");
myMap.put("Wednesday", "ghi");
myMap.put(AtEnd, "XYZ");
System.out.println("myMap: "+myMap);
// {Monday=abc, Tuesday=def, Wednesday=ghi, Always at the end=XYZ}
}
I wonder if you're looking for some variation on this?
source to share
You can achieve this using LinkedHashMap
as it guarantees that the results are returned in insert order.
Also check the following post to understand the difference between card types.
Or just create a custom class that contains a different key than value. Sort by key of this class. For your case, enter the key value as day, and for the "No categorized" case, make sure its key runs later than any of the other keys, for example, make it "Z_Not Categorized".
public ComplexKey
{
String key;
String value;
}
ComplexKey monday = new ComplexKey("monday", "monday");
ComplexKey notCategorized = new ComplexKey("Z_Not Categorized", "Not Categorized");
Then you can write your own comparator that sorts the values ββaccording to the class key complexKey
.
source to share
In fact, keys are always sorted. If you draw the card a couple of times, you will find that the result remains the same.
First, I'll gossip about hashing again:
The reason is hashing. Every object has a method hashCode()
. The hash space is like a large array that contains all the possible values ββof the hash function as indices. When a new element is inserted into HashSet
or a new pair is placed into HashMap
, it is placed in the hash space according to its hash code. If two elements have the same hash code, they will be compared with the method equals()
, if it is not equal, then the new element will be placed next to it.
Then, if you know what is going on there, you can implement some code like below:
import java.util.*;
class MyString {
private String str;
public MyString (String str) {
this.str = str;
}
public String toString () {
return str;
}
public boolean equals (Object obj) {
if (obj.getClass().equals(MyString.class)) {
return obj.toString().equals(str);
}
return false;
}
public int hashCode () {
if (str.equalsIgnoreCase("Not Categorized")) {
return Integer.MAX_VALUE;
} else if (str.hashCode() == Integer.MAX_VALUE) {
return 0;
}
return str.hashCode();
}
}
public class Test {
public static void main (String args[]) {
Map<MyString, String> m = new HashMap<MyString, String>();
m.put(new MyString("a"), "a");
m.put(new MyString("c"), "c");
m.put(new MyString("Not Categorized"), "NC");
m.put(new MyString("b"), "b");
Set<MyString> keys = m.keySet();
for (MyString k : keys) {
System.out.println(m.get(k));
}
}
}
The result "Not categorized" always comes. The reason is simple: the hash value is always the maximum integer.
The reason I am creating the String wrapper class is the String class is final, it cannot be extended. This way you will have a small change in the structure of your class, but not much.
TreeMap can be used, although it will be less efficient:
public static void main (String args[]) {
Map<String, String> m = new TreeMap<String, String>(new Comparator<String>() {
public int compare (String s1, String s2) {
if (s1.equals(s2)) {
return 0;
}
if (s1.equalsIgnoreCase("Not Categorized")) {
return 1;
}
if (s2.equalsIgnoreCase("Not Categorized")) {
return -1;
}
if (s1.hashCode() > s2.hashCode()) {
return 1;
} else if (s1.hashCode() < s2.hashCode()) {
return -1
} else {
return 0;
}
}
public boolean equals (Object obj) {
return false;
}
});
m.put("a", "a");
m.put("c", "c");
m.put("Not Categorized", "NC");
m.put("b", "b");
Set<String> keys = m.keySet();
for (String k : keys) {
System.out.println(m.get(k));
}
}
The result is the same. It will sort all the elements, but it will not change the hash order of other strings, it will make sure that "Not classified" is always the largest.
source to share