21

I have a Generic Class with two type variables, which implements java.lang.Comparable.

public class DoubleKey<K,J> implements Comparable<DoubleKey<K,J>>{

    private K key1;
    private J key2;

    public DoubleKey(K key1, J key2){
        this.key1 = key1;
        this.key2 = key2;
    } 

    public K getFirstKey(){
        return this.key1;
    }

    public J getSecondKey(){
        return this.key2;
    }

    // need for Comparable interface
    public int compareTo(DoubleKey<K,J> aThat){
        ...
    }

}

Becuase i implemeted it with Comparable, I need to write the compareTo() method. Because K, J can be of ANY type, I'm having problems on how to compare them completely. Is there a way to be able to catch all possible types (Primitive, Wrapper, Object) in the comparison? Thanks for the help!

John Bautista
  • 1,480
  • 3
  • 29
  • 60

6 Answers6

14

so to summarize the said above and to puzzle it together into a working code this is:

    public class DoubleKey<K extends Comparable<K>, J extends Comparable<J>>
        implements Comparable<DoubleKey<K, J>> {

    private K key1;
    private J key2;

    public DoubleKey(K key1, J key2) {
        this.key1 = key1;
        this.key2 = key2;
    }

    public K getFirstKey() {
        return this.key1;
    }

    public J getSecondKey() {
        return this.key2;
    }

    public int compareTo(DoubleKey<K, J> that) {

        int cmp = this.getFirstKey().compareTo(that.getFirstKey());
        if (cmp == 0)
            cmp = this.getSecondKey().compareTo(that.getSecondKey());
        return cmp;
    }
}
olippuner
  • 394
  • 4
  • 16
8

Would you like to introduce a requirement that K and J have a natural ordering that you can use? In this case you can declare your class DoubleKey like this:

class DoubleKey<K extends Comparable<K>, J extends Comparable<J>>

You can then define your DoubleKey's compareTo as you like. You can do things like:

getFirstKey().compareTo(aThat.getFirstKey())

You can't compare any instance of K to an instance of J, though. There is no ordering defined over those types.

If these types don't necessarily have a natural ordering (many don't), you can take a Comparator<K> and Comparator<J> as parameters to the constructor of your DoubleKey. A class that does this already that you can use as an example is Google Guava's excellent Maps class (see specifically the newTreeMap methods and the bounds of the types they accept).

sjr
  • 9,769
  • 1
  • 25
  • 36
  • what does **getFirstKey().compareTo(aThat.getFirstKey())** returns when 2 variables are equal, and if not equal? – John Bautista Feb 16 '11 at 08:30
  • 1
    This is just the standard `Comparable` contract. In `a.compareTo(b)`, if they are equal `compareTo` returns 0, if a < b returns a negative number (usually -1), if a > b returns a positive number (usually 1). – sjr Feb 16 '11 at 08:33
  • 2
    `K extends Comparable super K>` would be better, because it deals with subclasses of Comparable classes appropriately – newacct Feb 17 '11 at 10:50
4
public class DoubleKey<
        K implements Comparable<K>, 
        J implements Comparable<J>> 
    implements Comparable<DoubleKey<K,J>> {

    public int compareTo(DoubleKey<K,J> that){
        int cmp = this.key1.compareTo(that.key1);
        if(cmp==0) cmp = this.key2.compareTo(that.key2);
        return cmp;
    }
}
paulmurray
  • 3,355
  • 1
  • 22
  • 17
0

As is often the case, there exists a library that can solve your problem: Apache Commons lang3. I often use Pair<L,R> instances as keys. They implement Comparable.

Community
  • 1
  • 1
Michael Scheper
  • 6,514
  • 7
  • 63
  • 76
0

You'll have to define a rule when a DoubleKey<K,J> is smaller, bigger or equal to this one. That's what compare does. Maybe, that's my actual guess, it doesn't make much sense to compare to instances of DoubleKey<K,J>.

If you don't actual care how they're ordered and only need to implement any ordering, try this:

public int compareTo(DoubleKey<K,J> that){
    // real codes needs checks for null values!
    return (this.key1.toString() + this.key2.toString()).compareTo(that.key1.toString() + that.key2.toString());
}
Andreas Dolk
  • 113,398
  • 19
  • 180
  • 268
  • But then you should make sure to document this (that `compareTo` depends on the `toString()` of the parts), and that `compareTo` is not consistent to `equals`. – Paŭlo Ebermann Feb 16 '11 at 09:12
  • It does make sence if you use for example DoubleKey. You could then use compare to compare the Object values J how you wanted depending on what fields are in them by calling their own compare methods. For example size and weight. Alternatively, ou could compare the key strings alphabetically. It's all up to the developer of the abstract data type DoubleKey to implement behavior of their choosing and to document it well. – Andrew S Oct 05 '15 at 22:03
0

First way: use hashCodes, like

 public int compareTo(DoubleKey<K,J> aThat){
     getFirstKey().hashCode() + getSecondKey().hashCode() - aThat.getFirstKey().hashCode() +   aThat.getSecondKey().hashCode();
 }

(you should think more about formula)

Second way: add comparator to constructor

public DoubleKey(K key1, J key2, Comparator cmp){
Stan Kurilin
  • 15,614
  • 21
  • 81
  • 132
  • 1
    First way is wrong, because "_[It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results.](http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#hashCode())_", also the `int` in hashCode is arbitrary, you can't assume that an `Object` with hachCode 42 is greater than an `Object` with hashCode 1. –  Feb 19 '11 at 10:28
  • @Electro, Of course it's bad way. But, there isn't any criteria for comparing Object, so you can use Ordering by hashCodes) – Stan Kurilin Feb 19 '11 at 11:02