2014-09-23 77 views
1

基本上,我有一个通用BST,我想为Comparable对象和对象使用关联的Comparator类。例如,我希望该树可以与Integers一起使用,也可以用于Point2D,其中点可以按X方向或Y方向排序(为此我有相应的比较器)。我想知道做这个的标准方法是什么?我想出了这个:适用于可比较和比较器的泛型

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))); 
    }  
} 

其中Point2DCompare.Comparators.X是,通过他们的X坐标点排序一个简单的比较。这似乎工作,但它有点难看。我想知道是否有标准的方法来解决这个问题?

编辑:实现基于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:与第二个实施的一个问题是,它并不会导致编译错误,如果没有使用非可比Data比较。例如:

Test<Point2D> test3 = new Test<Point2D>(); 
System.out.println(test3.dataComparator.compare(new Point2D.Double(1,2),new Point2D.Double(2,2))); 

导致运行时错误。如果有一个完整的解决方案,这将是很好的。

+0

另一种选择很可能会创建一个调用的compareTo(),如果你没有通过一个比较器,但我不确定这会是更清洁还是更高效。你有什么看起来不错。 – Tim 2014-09-23 19:55:15

+0

@Tim我想我会坚持下去,直到更优雅的东西出现。 – Justin 2014-09-23 20:16:13

+0

第二个是非常错误的。泛型构造函数创建一个* new *泛型类型参数'Data',它独立于类,并隐藏类中的类型参数'Data'。因此,例如,您可以将“Data”重命名为“Data2”。它也是不安全的,因为创建的比较器将是'Comparator ',其中'Data2'是构造函数自己的'Data',它可以是任何东西(因为构造函数的签名在任何参数中都不使用'Data2')。而不是班级的“数据”。但是,变量'dataComparator'必须是'比较器','Data'是类的'Data'。 – newacct 2014-09-23 23:01:23

回答

1

唯一的类型安全的方法是使用静态工厂方法创建基于可比较的而不是构造函数的自然顺序的类。静态方法将是通用的,将只接受满足可比对自身的约束类型:

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); 
      } 
     }); 
    } 
} 
+0

只是好奇,是否有一种方法可以在比较器和“数据”之间强制执行类型安全?如在,你可以有'Test t = new Test(integerComparator)',对于我做的测试,它仍然可以编译。 – Justin 2014-09-24 02:35:07

+1

@jucestain:创建对象时使用的是原始类型。您需要使用'new Test (...)',或者在Java 7+中使用'new Test <>(...)'。 – newacct 2014-09-24 08:31:29

+0

谢谢!!!!!!!!!! – Justin 2014-09-25 03:38:10

0

创建一个默认比较器。默认比较器的行为是查看并查看两个对象是否为Comparable。如果这样比较,如果没有,则抛出异常。将此比较器分配给您的类中的实例变量。

然后你有两个选择。或者用一个可选的比较器创建类(在这种情况下,您可以替换默认的),并且您的比较代码只使用分配给该实例变量的比较器,或者您的比较方法可选地使用比较器,并使用传入的比较器或您的如果没有传递,则为默认。使用哪种方法将取决于你的班级如何被调用。

+0

你能提供一些非常基本的代码,所以我可以看到你的意思吗?我是一个Java新手。其实,我要为这个问题添加一个编辑,然后如果我实施的是您建议的内容,那么您可以确认一下吗? – Justin 2014-09-23 20:24:53

+0

当你有机会时,你可以检查编辑,让我知道这是你的意思吗?我真的很喜欢,因为它会允许我使用'Collections.sort'等。 – Justin 2014-09-23 20:37:40

+0

这不完全是类型安全的,因为两个对象可能是“可比较的”,但不能相互比较 – newacct 2014-09-23 22:58:33