2012-04-14 126 views
4

我是一個具有C++經驗的Java noob,我試着按照以下幾行創建Java集合(類似於C++中的做法):Java泛型和集合集

Set< Set<String> > collection = new TreeSet< Set<String> >(); 
Set<String> entry = new TreeSet<String>(); 
collection.add(entry); 

這建立良好,但之後在執行程序時,一個java.util.TreeSet cannot be cast to java.lang.Comparable拋出異常。

沒有重新實現輪子,怎麼能有一套在Java集?

此外,Java允許破壞代碼(例如,類型不匹配)編譯的交易是什麼?

在此先感謝您的任何反饋意見。

+0

我會在這種情況下使用HashSet(請參閱您的問題的biziclop解釋)。 – Raveline 2012-04-14 19:14:51

+0

你需要你的外套是一個排序集嗎?如果是這樣,排序規則是什麼? – nansen 2012-04-14 19:19:26

+0

順序無關緊要;我用TreeSet來說明我的問題。我的猜測是HashSet也可以做到這一點(儘管我經常對任何依賴啓發式算法(即散列函數)的算法保持警惕,當我對處理的數據不夠了解時) – Meh 2012-04-14 19:22:47

回答

6

在合同TreeSet中,要求規定所有條目必須是Comparable或者您必須提供Comparator。 (這也是爲什麼你沒有看到一個編譯時錯誤:條目只能轉換爲Comparable在沒有明確的Comparator的)

它什麼都沒有做與仿製藥,它來自的TreeSet實施本身:因爲它是一棵二叉樹,只有條目可以以某種方式排序纔有意義。

如果您更多地瞭解您的具體問題,我們可以幫助您找到所需的確切數據結構,但一般來說,如果您不關心組中元素的順序,則使用HashSet 。總的來說,SetSet通常是一個馬虎設計的標誌。

+0

感謝您的提示。我可能會實現一個自定義比較器。 – Meh 2012-04-14 19:35:32

+0

具體問題實際上就像上面發佈的示例問題一樣簡單。我需要跟蹤唯一的字符串標記集,然後將其打印出來。所以這是一個非常簡單的問題,並且在我看來,編寫各種包裝類會超出它。至於設計,好吧,我還是希望有時候可以選擇自己拍一下腳:) – Meh 2012-04-14 19:41:12

+0

@Meh然後你的情況是在1%的情況下使用一組套件是有道理的。 :) – biziclop 2012-04-14 19:50:00

2

如果要將對象添加到TreeSet集合中,該對象的類型必須實現Comparable接口,而該接口本身不包含此接口。或者,您可以通過使用不同的構造函數創建TreeSet來提供Comparator

對於這個特定場景使用TreeSet並沒有什麼意義,因爲按定義它是一個有序集合,並且您似乎不需要元素的排序。您可以嘗試使用HashSet

此外,要回答第二個問題,此錯誤僅在運行時出現,因爲您正在利用多態行爲,即您正在將Set添加到實際運行時綁定到TreeSet。這些信息在編譯時是不知道的。

+0

這不是100%正確的,因爲你也可以提供一個明確的比較器。 – biziclop 2012-04-14 19:18:08

+0

@biziclop:謝謝,我已經添加了該信息的完整性。 – Tudor 2012-04-14 19:20:58

0

的問題是,TreeSet並非完全「類型安全」從仿製藥的角度來看,因爲它需要能夠接受一個自定義的比較使用自然排序。

如果TreeSet只使用自然順序,則可以聲明爲TreeSet<E extends Comparable<? super E>>並且它是類型安全的 - 不能與其自身相比的類型不能用作參數。另一方面,如果它總是使用比較器,那麼它也是類型安全的。

但它現在設計的方式允許您創建一個不帶比較器的TreeSet(因此使用自然排序),並且使用與其本身無法比較的元素類型。沒有編譯時檢查來執行此操作。只有在運行時纔會發現故障。

實際上有一種方法可以修復TreeSet。它可以同時支持自然排序和定製的比較和是類型安全的:

  • 必須能夠接受一個自定義比較

  • 有沒有構造自然排序情況下的構造函數。相反,請使用工廠方法創建使用自然排序的TreeSet。工廠方法可以有約束<E extends Comparable<? super E>>一個泛型類型,需要類型可媲美本身

我不知道爲什麼Java庫的設計並沒有做這種方式。