1

I've got a hashmap<CustomObject,Integer> and I'd like to compare the Integers (values) within each entry. So, basically I'd like to sort my values by their Integer value in descending order. I've got a Comparator which consists of the following...

class Compare implements Comparator<Integer>{
    Map<CustomObject,Integer> map;
    /**
     * Constructs our map
     * @param map map to be sorted.
     */
    public Compare(Map<CustomObject,Integer> map){
        this.map = map;
    }
    /**
     * Performs the comparison between two entries.
     */
    public int compare(Integer one, Integer two){
        if(map.get(one) <= map.get(two)){
            return 1;
        }else{
            return 0;
        }
    }
}

I pass my Hashmap into the TreeMap by calling the following line of code.. Tmap.putAll(Hmap);. Where Tmap and Hmap are defined as:

private HashMap<CustomObject,Integer> Hmap;
private TreeMap<CustomObject,Integer> Tmap;

When I run my code I get the error Exception in thread "main" java.lang.ClassCastException: CustomObject cannot be cast to java.lang.Comparable.

The exception appears to be called when I attemp to extract a value from my sorted list. like so...

TreeMap<CustomObject,Integer> sorted = Tmap.putAll(hmap);
sorted.get(o);

where o is a CustomObject.

I think I've misunderstood how comparator works.. what am I doing wrong? How would I compare the two Integer values?

EDIT

Just to clarify what I'm actually trying to do...

I want to compare Integers which are linked to a CustomObject. I can't make the key the Integer because these Integers may not be unique. I was to compare them because I'd like to sort my collection in descending order based on their Integer value.

Skizit
  • 43,506
  • 91
  • 209
  • 269
  • Where is the code where the exception is thrown?? – SJuan76 Dec 16 '11 at 12:50
  • The problem is that a Comparator for maps compares keys, not values. I don't see any solution to your problem apart from doing a reverse map, then go over it, then insert the result in a LinkedHashMap. Not what you want, I expect... – fge Dec 16 '11 at 12:52
  • @SJuan76 I've updated my code to reflect that – Skizit Dec 16 '11 at 12:53
  • @fge What would you suggest the best way to sort a collection based on Integer value in a Key,Value pair? – Skizit Dec 16 '11 at 12:53
  • Well, I just found this: http://stackoverflow.com/questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java – fge Dec 16 '11 at 12:58
  • Otherwise, creating a custom SortedMap implementation! Hard, but why not. I can have a go at it if you want. – fge Dec 16 '11 at 12:59

6 Answers6

5

You need to change your comparator to compare CustomObjects, not Integers:

class Compare implements Comparator<CustomObject>{
    Map<CustomObject,Integer> map;
    /**
     * Constructs our map
     * @param map map to be sorted.
     */
    public Compare(Map<CustomObject,Integer> map){
        this.map = map;
    }
    /**
     * Performs the comparison between two entries.
     */
    public int compare(CustomObject left, CustomObject right){
        return map.get(left).compareTo(map.get(right));
    }
}

Then, you need to tell the TreeMap to use your comparator:

private Map<CustomObject,Integer> Tmap = 
    new TreeMap<CustomObject,Integer>(new Compare(HMap));
Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
  • but.. I need to compare the Integer values not their keys(CustomObjects) – Skizit Dec 16 '11 at 12:58
  • 1
    @Skizit: That is why you are calling `map.get` in the comparator. This retrieves the integer-value corresponding to the `CustomObject`. The result is that the `CustomObject`s are ordered by their corresponding Integer-values. – Björn Pollex Dec 16 '11 at 13:01
1
new TreeMap<..>(new Compare<..>(map))

You have to specify the comparator when constructing the tree. Otherwise it assumes your keys are comparable (and they aren't)

But check this answer for sorting a map based on the values.

Community
  • 1
  • 1
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
1

This approach has a few problems.

  • TreeMap ignores duplicates (when compareTo returns 0) In your case it will only add decreasing integer values. You can fix it so the numbers can be in any order, but it would still drop any entries with duplicate values.
  • The fields used in the compareTo cannot change or will corrupt the collection.
  • You need to know the whole key to lookup a value. In this case, you need to know the value in this case to look it up which may not be very useful.

You are better off creating a List<Entry<CustomObject,Integer>> from the map.entrySet() which you can sort, as this allows duplicates and is ordered.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
1

I think that the problem will be that you are using the wrong TreeMap constructor. The one you are using requires that all keys are instances of classes that implement Comparable. Your CustomObject does not. You should use the constructor that takes a Comparator parameter; e.g.

TreeMap<CustomObject,Integer> tmap = 
    new TreeMap<CustomObject,Integer>(new Compare());

This will also tell you that your Compare class needs to implement Comparator<CustomObject> not Comparator<Integer>.

Another problem is that your comparator is not implementing the correct semantics. The compare method should return a -ve number if arg1 < arg2, zero if arg1 = arg2 and a +ve number if arg1 > arg2; e.g.

public int compare(CustomObject one, CustomObject two){
    return Integer.compare(map.get(one), map.get(two));
}

And even this is dodgy:

  • If any two CustomObject instances that map to the same integer will be treated as equal, and you won't be able to have both as (distinct) keys in the TreeMap.

  • Your comparator will throw an NPE if there is no entry in map for one or two.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
1

I would suggest using a multimap indexed by integer. If you need to retain the ability to look up these pairs by object you can maintain both maps. Java doesn't ship with a multimap but there are easy solutions. Here is an example of Map<Integer,List<Object>> (scroll down to the multimaps section).

Paul Jackson
  • 2,077
  • 2
  • 19
  • 29
0

First thin is that the key should be Integer and value should be CustomObject and then the HashMap can be sorted based on the comparator. But by default the HashMap or if you wan to sort on CustomObject then you have to make the CustomObject implements Comparable and write the compare method in that which will make the HashMap to sort based on CustomObject. If you have understood this. and want to try your self try. or if you want me to explain with an example I can do it.

The question is confusing.