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?

+3


source to share


4 answers


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?

+5


source


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.

Difference between HashMap, LinkedHashMap and TreeMap



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

.

+3


source


In your case, I would use TreeMap:

Map<DayOfWeek, Object> favoritesMap = new TreeMap<>();

      

where DayOfWeek

is the class that you declare like this:

class DayOfWeek implements Comparable<DayOfWeek> {

      

as it's awkward to sort wooks days as strings.

+1


source


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.

+1


source







All Articles