5

我是一个有一点 C++ 经验的 Java 菜鸟,我正在尝试按照以下方式在 Java 中创建一组集合(类似于在 C++ 中所做的):

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 中拥有一组 Set?

此外,Java 允许编译损坏的代码(例如,类型不匹配)是怎么回事?

提前感谢您的任何反馈。

4

3 回答 3

7

在 的合同中TreeSet,要求所有条目必须是Comparable或您必须提供Comparator. (这也是您没有看到编译时错误的原因:只有Comparable在没有明确的. 时才会转换为条目Comparator。)

它与泛型无关,它来自TreeSet自身的实现:因为它是一棵二叉树,所以只有条目可以以某种方式排序才有意义。

如果您详细说明您的具体问题,我们可能会帮助您找到所需的确切数据结构,但一般来说,如果您不关心集合中元素的顺序,HashSet则使用 a。同样,一般来说,a Setof Sets 通常是设计草率的标志。

于 2012-04-14T19:13:05.333 回答
2

如果要将对象添加到TreeSet集合中,则该对象的类型必须实现Comparable接口,而接口TreeSet本身没有。或者,您可以Comparator通过使用不同的构造函数创建 TreeSet 来提供。

在这种特殊情况下使用 a 并没有真正意义,TreeSet因为根据定义,这是一个有序集合,您似乎不需要元素的排序。你可以试试 a HashSet

此外,要回答您的第二个问题,此错误仅在运行时出现,因为您正在利用多态行为,即您正在添加到 a Set,实际上在运行时绑定到 a TreeSet。此信息在编译时未知。

于 2012-04-14T19:15:38.093 回答
0

问题在于,TreeSet从泛型的角度来看,它并不是完全“类型安全”的,因为它需要能够接受自定义比较器使用自然排序。

如果TreeSet只使用自然顺序,它可以被声明为TreeSet<E extends Comparable<? super E>>并且它是类型安全的——与自身不可比较的类型根本不能用作参数。另一方面,如果它总是使用比较器,它也是类型安全的。

但是它现在的设计方式允许您创建一个TreeSet没有比较器(因此使用自然排序)的元素类型与自身不可比较。没有编译时检查来强制执行此操作。仅在运行时才注意到失败。

实际上有一种方法TreeSet可以修复。它可以支持自然排序和自定义比较器,并且是类型安全的:

  • 有一个接受自定义比较器的构造函数

  • 没有自然排序情况的构造函数。相反,有一个工厂方法来创建一个TreeSet使用自然排序的。工厂方法可以有一个泛型类型绑定<E extends Comparable<? super E>>,它要求类型与自身具有可比性

我不知道为什么 Java 库设计者不这样做。

于 2012-04-15T02:22:15.613 回答