0

I am writing the below code to sort the hash map values :

private static HashMap sortByValues(HashMap map) { 
    List list = new LinkedList(map.entrySet());
    Collections.sort(list, new Comparator() {
        public int compare(Object o1, Object o2) {
            return (((Map.Entry) (o1)).getValue()).compareTo(((Map.Entry) (o2)).getValue());
        }
    });
}

However When I execute this, it throws an error stating cannot find symbol compareTo. But isnt the method in the String class which is in the lang package?

Also when i replace it by adding a Comparable typecast, it runs fine

private static HashMap sortByValues(HashMap map) { 
    List list = new LinkedList(map.entrySet());
    Collections.sort(list, new Comparator() {
        public int compare(Object o1, Object o2) {
             return ((Comparable) ((Map.Entry) (o1)).getValue()).compareTo(((Map.Entry) (o2)).getValue());
        }
    });
}

Can someone please help, I am a beginner in Collections.

Gholamali Irani
  • 4,391
  • 6
  • 28
  • 59
ghostrider
  • 2,046
  • 3
  • 23
  • 46
  • 4
    Step 1 - learn about generics. If you don't know generics, you won't understand any correct answers that get posted here. – Dawood ibn Kareem Dec 20 '17 at 02:38
  • Possible duplicate of [What is a raw type and why shouldn't we use it?](https://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it) – azurefrog Dec 20 '17 at 02:38
  • I am trying to sort by values not keys – ghostrider Dec 20 '17 at 02:43
  • @DawoodibnKareem compareTo method exists in String class as well.Over here I am sorting by string values. So shouldn't the compareTo without the Comparable typecast work fine? – ghostrider Dec 20 '17 at 02:49
  • 1
    No, not really. The compiler has no guarantee that the things in your HashMap are Strings. So even though your code might be fine at run time, the compiler is protecting you from trying to compare things that can't be compared. You should be using generics to tell the compiler that the things in the HashMap all implement Comparable. I might write an answer later if I have time (I don't like rab's answer very much), but really, the best thing for you to do is to learn how to use generics, as I said earlier. – Dawood ibn Kareem Dec 20 '17 at 02:51
  • You declare `sortByValues` to return a `HashMap` but are not returning anything. You probably mean to return a `List`, but without a `return` statement this is not valid Java. – Jim Garrison Dec 20 '17 at 02:54
  • Yes, I was about to make a similar comment to Jim Garrison. The code snippet that you claim "runs fine" doesn't actually compile. – Dawood ibn Kareem Dec 20 '17 at 02:55
  • @DawoodibnKareem I haven't posted the entire snippet. I am having issues with the given Typecast only..Yes it would be very helpful if you could write an answer.Meanwhile I will start with the generics as suggested.. – ghostrider Dec 20 '17 at 02:58
  • You might get better answers if you post that entire method. – Dawood ibn Kareem Dec 20 '17 at 03:01

3 Answers3

2

All of Java's collection classes and interfaces are generics, which means they are intended to be used with type parameters. For historical reasons it's possible to use them without type parameters, which is what you've done here. However, it's a bad idea to do that, because

  • you sacrifice a lot of the type safety that the compiler gives you,
  • you end up casting objects to whatever type you need them to be, which is prone to error, and such errors usually only reveal themselves at run time.

Finding errors at compile time is better than finding them at run time. The compiler is your friend - it gives you messages to help you find your errors.

Now in this particular case, you seem to be building a sorted list of values from your map. But it only makes sense to do this if the values in your map belong to some type that can be sorted. There's no general way of sorting Object, so for this to make sense, you want to restrict your parameter to be a map whose values can be sorted, or to put it another way, can be compared to other objects of the same type.

The generic interface that tells you that one object can be compared to another is Comparable. So if you have a type V, then writing V extends Comparable<V> means that one object of type V can be compared to other objects of type V, using the compareTo method. This is the condition that you want the type of the values in your map to obey. You don't need any such condition on the type of the keys in your map.

Therefore, you could write your method as generic, which means that its signature will list some type parameters, inside < > characters, possibly with some conditions on those type parameters. In your case, you'd give your method a signature like this, assuming it's going to return a List.

private static <K, V extends Comparable<V>> List<V> sortAndListValues(Map<K,V> map)

Of course, if you really intend to return some kind of sorted map, then it might be more like

private static <K, V extends Comparable<V>> Map<K,V> sortByValues(Map<K,V> map)

but you need to remember that it's not possible to sort HashMap objects. They're naturally sorted in an order that's implied by the hashCode function of the key class, and by the current size of the map. This is generally not a very useful order. There are other types of map in the JDK, such as

  • TreeMap, which sorts its entries according to the key - not what you want here
  • LinkedHashMap, which sorts its entries according to the order they were inserted - and you could probably make use of this here.

For the sake of answering your question though, I'm just going to write the List version of your method.

private static <K, V extends Comparable<V>> List<V> sortAndListValues(Map<K,V> map) {
    List<V> toReturn = new LinkedList<>(map.values());
    Collections.sort(toReturn, new Comparator<V>() {
        public int compare(V first, V second) {
             return first.compareTo(second);
        }
    });

    return toReturn;
}

Note that by using the type parameters K and V wherever it's appropriate to do so, there's no need for any kind of casting. The compiler will also warn you if you try to use any of the objects in the map in a way that's inappropriate for their type.

There are shorter ways of writing this of course, using the "functional style" that comes with Java 8. But that's a topic for another post entirely.

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
1

@ghostrider - you have removed generics from HashMap so both key and value are of Object type. Inside contents of map are Comparable type but the reference is of Entry<Object, Object> not Entry<Object, Comparable>. Look into the below example.

Object obj = new Integer(5);
int i = obj.intValue();  // Error
int i = ((Integer)obj).intValue(); // Success

Here int i = obj.intValue(); fails but int i = ((Integer)obj).intValue(); get success because i am explicitly type casting because of reference is of Object type.

sanit
  • 1,646
  • 1
  • 18
  • 21
  • This seems good. I have suggested to use Generics to implement the logic. Don't you think the above typecast seems more neat? Can you tell me is there anything I am missing with this bias? – ghostrider Dec 20 '17 at 03:04
  • 1
    Generics always being a nice feature so i will encourage you to use generics wherever possible. It gives both `Compile time safety` and `No need to do explicit type castings`. – sanit Dec 20 '17 at 03:22
  • 1
    "Neat" is subjective. What you are missing is that by not using generics, you prevent the compiler from pointing out problems that would manifest as runtime errors. – Stephen C Dec 20 '17 at 03:53
-2

You can do this by following

private static Map<String, Integer> sortByValue(Map<String, Integer> unsortMap) {

        // 1. Convert Map to List of Map
        List<Map.Entry<String, Integer>> list =
                new LinkedList<Map.Entry<String, Integer>>(unsortMap.entrySet());

        // 2. Sort list with Collections.sort(), provide a custom Comparator
        //    Try switch the o1 o2 position for a different order
        Collections.sort(list, new Comparator<Map.Entry<String, Integer>>() {
            public int compare(Map.Entry<String, Integer> o1,
                               Map.Entry<String, Integer> o2) {
                return (o1.getValue()).compareTo(o2.getValue());
            }
        });

        // 3. Loop the sorted list and put it into a new insertion order Map LinkedHashMap
        Map<String, Integer> sortedMap = new LinkedHashMap<String, Integer>();
        for (Map.Entry<String, Integer> entry : list) {
            sortedMap.put(entry.getKey(), entry.getValue());
        }




        return sortedMap;
    }
rab
  • 207
  • 2
  • 6
  • Thanks for the answer..the method in the question seems more neat I think, is it possible to explain what is wrong with my approach? – ghostrider Dec 20 '17 at 02:44
  • can you please check http://forums.codeguru.com/showthread.php?290458-compareTo-problem-Please-help-ASAP! – rab Dec 20 '17 at 02:50