In Java, sort hash map by its key.length()
Asked Answered
T

6

6

i have a hashmap like this:

HashMap<String,Integer> map = new HashMap<String,Integer>();
map.put("java",4);
map.put("go",2);
map.put("objective-c",11);
map.put("c#",2);

now i want to sort this map by its key length, if two keys length are equal (e.g go and c# both length 2), then sorted by alphba order. so the outcome i expect to get is something like:

printed result: objective-c, 11 java, 4 c#, 2 go, 2

here is my own attamp, but it doesnt work at all...

      HashMap<String,Integer> map = new HashMap<String,Integer>();
          map.put("java",4);
          map.put("go",2);
          map.put("objective-c",11);
          map.put("c#",2);

      Map<String,Integer> treeMap = new TreeMap<String, Integer>(
                new Comparator<String>() {
                    @Override
                    public int compare(String s1, String s2) {
                        return s1.length().compareTo(s2.length());
                    }
                }
        );

actually the 'compareTo' method appears as red (not be able to compile).... please someone help me with some code example...i am a bit confusing with how to use comparator class to customize compare object...

Tullus answered 17/9, 2014 at 20:38 Comment(0)
S
16

The compiler is complaining because you cannot call compareTo on an int. The correct way to sort the map is the following:

Map<String, Integer> treeMap = new TreeMap<String, Integer>(
    new Comparator<String>() {
        @Override
        public int compare(String s1, String s2) {
            if (s1.length() > s2.length()) {
                return -1;
            } else if (s1.length() < s2.length()) {
                return 1;
            } else {
                return s1.compareTo(s2);
            }
        }
});

The first two conditions compare the lengths of the two Strings and return a positive or a negative number accordingly. The third condition would compare the Strings lexicographically if their lengths are equal.

Suburbia answered 17/9, 2014 at 20:42 Comment(0)
A
6

You call String#length(), which returns a primitive int. You need the static method Integer.compare(int,int). If you are on Java 8, you can save yourself a lot of typing:

Map<String,Integer> treeMap = new TreeMap<>(
        Comparator.comparingInt(String::length)
                  .thenComparing(Function.identity()));
Acreinch answered 17/9, 2014 at 20:45 Comment(5)
sir your out put is {}?Lhary
My code doesn't attempt any output, but if you're asking about the String representation of an empty map, then you are right.Acreinch
I posted this question. if you can answer #25905945Lhary
This answer is not correct, if you have two keys "Foo" and "foo", i.e. same length and same letters with different case then the second put will override the first one, that is why there should be a lexicographic comparison if the length is equal.Markham
@Markham True, it was an omission. I update the answer so that the comparison falls back to the default string comparison.Acreinch
J
2
public int compare(String o1, String o2) {
  return o1.length() == o2.length() ? o1.compareTo(o2) : o1.length() - o2.length();
}
Jd answered 1/8, 2015 at 13:42 Comment(0)
A
1

because length() doesn't define compareTo method thats why you see error. To correct it use Integer.compare(s1.length(), s2.length()); updated code below

import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

public class Test {

    public static void main(String[] args) {

        HashMap<String,Integer> map = new HashMap<String,Integer>();
        map.put("java",4);
        map.put("go",2);
        map.put("objective-c",11);
        map.put("c#",2);


        Map<String,Integer> treeMap = new TreeMap<String, Integer>(
                new Comparator<String>() {
                    @Override
                    public int compare(String s1, String s2) {
                        return Integer.compare(s1.length(), s2.length());
                    }
                }
        );

        treeMap.putAll(map);

        System.out.println(treeMap);
    }
}
Attica answered 17/9, 2014 at 20:42 Comment(4)
when you run your code, the outcome is {c#=2, java=4, objective-c=11}. the question is where is the "go",2?Lhary
Its due to the fact that TreeMap is using Comparator and comparator is defined to treat 2 strings of equal length as same. Since OP was playing with TreeMap so i extended his/her answer to correct only the Comparator part.Attica
oh cool what do you think about my answer? since the op did not mention that using treemap is mandatory I used another styleLhary
code is not working as expected, keys with the same length got overridenJd
L
1

If using the TreeMap is not mandatory

Explantion: Define a Comaprator , and next step, define a list so we can add all map entries into a list. At the end, sort the list by defined Comaprator

Code:

 Comparator<Map.Entry<String,Integer>> byMapValues = 
         (Map.Entry<String,Integer> left, Map.Entry<String,Integer> right) ->left.getValue().compareTo(right.getValue());

 List<Map.Entry<String,Integer>> list = new ArrayList<>();
 list.addAll(map.entrySet());
 Collections.sort(list, byMapValues);
 list.forEach( i -> System.out.println(i));

Output:

c#=2
go=2
java=4
objective-c=11

Note: get sorted by number

if there is need to do comparison based on key, the following line can be used.

Comparator<Map.Entry<String,Integer>> byMapKeys = 
             (Map.Entry<String,Integer> left, Map.Entry<String,Integer> right) -> left.getKey().compareTo(right.getKey());
Lhary answered 17/9, 2014 at 21:10 Comment(0)
M
-2

The Comparator should be:

new Comparator<String>() {
    @Override
    public int compare(String s1, String s2) {
        return Integer.compare(s1.length(), s2.length());
    }
}
Merely answered 17/9, 2014 at 20:45 Comment(1)
Does not work. If the two strings are of same length, and using a TreeMap to sort, it will consider String keys of the same length as duplicates and remove them.Schizophyceous

© 2022 - 2024 — McMap. All rights reserved.