Basically, I have a generic BST that I'd like to work for both Comparable
objects and objects with an associated Comparator
class. For example, I'd like the tree to work with Integers
and also work for Point2D
, where the points can be sorted by X-direction or Y-direction (for which I have corresponding comparators). I was wondering what the standard way to do this was? I came up with this:
public class Test <Data> {
public <Data extends Comparable> Test() {
System.out.println("Comparable");
this.c = null;
}
public Test(Comparator<Data> comparator) {
System.out.println("Comparator");
this.c = comparator;
}
public int compare(Data d1, Data d2) {
if (c == null) {
return ((Comparable)d1).compareTo(d2);
} else {
return this.c.compare(d1,d2);
}
}
Comparator<Data> c;
public static void main(String[] args) {
Test<Integer> test = new Test<Integer>();
System.out.println(test.compare(1,2));
Test<Point2D> test2 = new Test<Point2D>(Point2DCompare.Comparators.X);
System.out.println(test2.compare(new Point2D.Double(1,2),new Point2D.Double(2,2)));
}
}
where Point2DCompare.Comparators.X
is a simple comparator that sorts points by their X-coordinate. This seems to work but it's a little ugly. I was wondering if there is a standard way to approach this issue?
EDIT: Implementation based on user1676075:
public class Test <Data> {
public <Data extends Comparable> Test() {
dataComparator = new Comparator<Data>() {
@Override
public int compare(Data p1,Data p2) {
return p1.compareTo(p2);
}
};
}
public Test(Comparator<Data> comparator) {
dataComparator = comparator;
}
public final Comparator dataComparator;
public static void main(String[] args) {
Test<Integer> test = new Test<Integer>();
System.out.println(test.dataComparator.compare(1,2));
Test<Point2D> test2 = new Test<Point2D>(Point2DCompare.Comparators.X);
System.out.println(test2.dataComparator.compare(new Point2D.Double(1,2),new Point2D.Double(2,2)));
}
}
EDIT2: One problem with the 2nd implementation is that it does not result in a compiler error if a non-comparable Data
is used without a comparator. For instance:
Test<Point2D> test3 = new Test<Point2D>();
System.out.println(test3.dataComparator.compare(new Point2D.Double(1,2),new Point2D.Double(2,2)));
results in a run-time error. Would be nice if there was a complete solution for this.
The only type-safe way to do it is to have a static factory method for creating the class based on the natural ordering of a comparable, instead of a constructor. The static method will be generic and will only accept types that satisfy the comparable-to-itself constraint:
public class Test<Data> {
Comparator<Data> c;
public Test(Comparator<Data> comparator) {
System.out.println("Comparator");
this.c = comparator;
}
public static <T extends Comparable<? super T>> Test<T> createWithComparable() {
System.out.println("Comparable");
return new Test<T>(new Comparator<T>() {
@Override
public int compare(T p1,T p2) {
return p1.compareTo(p2);
}
});
}
}