2012-04-14 183 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库的设计并没有做这种方式。