2014-09-23 107 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