0

Background

Create a Map that can be sorted by value.

Problem

The code executes as expected, but does not compile cleanly:

http://pastebin.com/bWhbHQmT

public class SortableValueMap<K, V> extends LinkedHashMap<K, V> {
  ...
  public void sortByValue() {
      ...
      Collections.sort( list, new Comparator<Map.Entry>() {
          public int compare( Map.Entry entry1, Map.Entry entry2 ) {
            return ((Comparable)entry1.getValue()).compareTo( entry2.getValue() );
          }
      });
  ...

The syntax for passing Comparable as a generic parameter along to the Map.Entry<K, V> (where V must be Comparable?) -- so that the (Comparable) typecast shown in the warning can be dropped -- eludes me.

Warning

Compiler's cantankerous complaint:

SortableValueMap.java:24: warning: [unchecked] unchecked call to compareTo(T) as a member of the raw type java.lang.Comparable

   return ((Comparable)entry1.getValue()).compareTo( entry2.getValue() );

Question

How can the code be changed to compile without any warnings (without suppressing them while compiling with -Xlint:unchecked)?

Related

Thank you!

Community
  • 1
  • 1
Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315

3 Answers3

6

Declare the V type to extend the Comparable<V> interface. That way, you can remove the cast of the Map.Entry objects down to (Comparable) and use the inferred type instead:

public class SortableValueMap<K, V extends Comparable<V>>
             extends LinkedHashMap<K, V> {

....

    Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
        public int compare(Map.Entry<K, V> entry1, Map.Entry<K, V> entry2) {
            return entry1.getValue().compareTo(entry2.getValue());
        }
    });
mhaller
  • 14,122
  • 1
  • 42
  • 61
2

The value should be a subclass of comparable.

SortableValueMap<K, V extends Comparable>

Try the above.

Ravi
  • 301
  • 1
  • 2
  • 8
1

The syntax for passing Comparable as a generic parameter along to the Map.Entry (where V must be Comparable?) -- so that the (Comparable) typecast shown in the warning can be dropped -- eludes me.

How about:

public class SortableValueMap <K, V extends Comparable<V>> extends LinkedHashMap<K, V> { 
  ...
    Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
        public int compare(Map.Entry<K, V> entry1, Map.Entry<K, V> entry2) {
            return (entry1.getValue()).compareTo(entry2.getValue());
        }
    });

but this may be better, depending on your intent:

public class SortableValueMap <K, V extends Comparable<? super V>> extends LinkedHashMap<K, V> { ...

See http://download.oracle.com/javase/tutorial/extra/generics/morefun.html

It isn't necessary that T be comparable to exactly itself. All that's required is that T be comparable to one of its supertypes. This give us:

public static <T extends Comparable<? super T>>  max(Collection<T> coll)

... This reasoning applies to almost any usage of Comparable that is intended to work for arbitrary types: You always want to use Comparable <? super T> . ...

Bert F
  • 85,407
  • 12
  • 106
  • 123
  • I went down the road of `V extends Comparable super V>` without success. After making the changes suggested by mhaller, I am able to use `super V`. Thank you! – Dave Jarvis Jan 02 '11 at 05:56
  • @Dave - sorry about that. I was lazy about testing it in my environment before posting. – Bert F Jan 02 '11 at 05:57
  • All good, Bert. I needed to change how `Collections.sort` was being called, as per mhaller's answer. Unfortunately, I cannot mark both answers as correct. +1 though! – Dave Jarvis Jan 02 '11 at 05:59
  • @Dave - Thanks. I just wanted to make sure the answer was correct or make sure I withdraw it. I ran the code through eclipse and added the same missing generic arguments that @mhaller added that I missed the first time. I updated the top part of my answer (now the same mhaller's) and I'll leave my answer for future readers to note the "You always want to use `Comparable super T>`" reference. – Bert F Jan 02 '11 at 06:06