我有以下代码,我试图将StringBuffer对象作为键放入TreeSet中。我这样做的原因是看是否可以将可变对象用作键。我没有任何编译错误。但是,当我运行此代码时,出现了代码下方的错误。特别是,我得到了这个java.lang.StringBuffer cannot be cast to java.lang.Comparable
。此错误表示什么?
从javadoc中我看到StringBuffer类被声明为final(public final class StringBuffer
),这并不意味着它是不可变的,因此是可哈希的?
我是哈希和不可变内容的新手,请在这里帮助我。
谢谢
import java.util.*;
class MutableKeys {
public static void main(String[] args) {
StringBuffer one = new StringBuffer("one");
StringBuffer two = new StringBuffer("two");
StringBuffer three = new StringBuffer("three");
Set<StringBuffer> sb=new TreeSet<StringBuffer>();
sb.add(one);
sb.add(two);
sb.add(three);
System.out.println("set before change: "+ sb);
one.append("onemore");
System.out.println("set After change: "+ sb);
}
}
Exception in thread "main" java.lang.ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.Comparable
at java.util.TreeMap.put(TreeMap.java:542)
at java.util.TreeSet.add(TreeSet.java:238)
at inheritance.MutableKeys.main
只需添加一个比较器类,然后按如下所示在TreeSet中使用它:
class Comparatorbuff implements Comparator<StringBuffer> {
@Override
public int compare(StringBuffer s1, StringBuffer s2) {
return s1.toString().compareTo(s2.toString());
}
}
in your main method: modify as follows
Set<StringBuffer> sb=new TreeSet<StringBuffer>(new Comparatorbuff());
StringBuffer
是public final class StringBuffer
的事实意味着您不能将其子类化。 StringBuffer非常可变(这很重要,您可以修改缓冲区的内容。)
您不想使用可变的键作为键,因为在修改对象之后,其equals()和hashcode()方法将返回不同的结果,并且您将无法在其中找到它地图了。
如果您真的想在TreeSet中使用StringBuffer,则必须提供自己的Comparator,因为StringBuffer不实现Comparable。
问题是TreeSet
对您放入其中的项目进行排序。因为StringBuffer
没有实现Comparable
,所以TreeSet
不知道如何对它们进行排序。创建Comparator
时应传递TreeSet
。您的比较器将告诉TreeSet
如何对StringBuffer
进行排序。您可以使用HashSet
,它不会对元素进行排序。
就不变性而言:类声明中的final关键字表示您不能对其进行子类化(扩展)。它本身不会使该类不可变。不可变是指对象的状态一旦创建便无法更改。 StringBuffer
创建后肯定可以更改其状态,因此它们不是一成不变的。
声明一个类final
并不意味着它是不可变的,它意味着不允许任何类对其进行子类化。实际上,StringBuffer
非常易变。这就是课程的重点。
因为StringBuffer
不是Comparable
,所以您的TreeSet
不知道如何对StringBuffers
进行排序。但是,将可变对象作为任何类型的Set
(或Map
)的键是一个坏主意。如果必须使用TreeSet
,则创建并使用比较Comparator
对象的自定义StringBuffer
对象。
TreeSet
仅采用Comparable
对象,而StringBuffer
不是Comaprable
对象。
Throws-ClassCastException-如果无法将指定对象与此集合中当前的元素进行比较。
您可以使用String
对象(因为字符串是可比较的)而不是StringBuffer
对象。例如:
Set<String> sb=new TreeSet<String>();
sb.add(one.toString());
sb.add(two.toString());
sb.add(three.toString());
System.out.println("set before change: "+ sb);
System.out.println("set After change: "+ sb);
您在问几个问题:
您有些困惑,我会帮助您进行整理
[Java中有2种识别策略(或多或少)。)>
哈希:将输入“ Foo”转换为最佳尝试生成一个唯一访问数组索引的数字。 (纯粹主义者,请不要滥用我,我有意简化)。该索引是您的值存储的地方。 “ Foo”和“ Bar”实际上可能生成相同的索引值,这意味着它们都将被映射到相同的数组位置。显然,这是行不通的,因此这就是“ equals()”方法的所在。它用于消除歧义
比较:通过使用比较方法,您不需要执行额外的歧义步骤,因为比较从不首先产生这种冲突。 “ Foo”等于的唯一键是“ Foo”。一个非常好的主意是,如果可以的话,是将“ equals()”定义为compareTo()== 0;为了一致性。不是必需的。
现在是您的一般性问题:
映射键可以可变吗?答:是的,非常非常糟糕和愚蠢。示例:Map.put(k,v); k.modifyInternalHash(); Map.get(k)= null; //这里不好实际上,这是通过哈希的粗心而发生的。尽管“比较地图”可能会发生这种情况,但诊断起来会容易得多。
StringBuffer可以用作TreeMap / Set的键吗?是。使用备用构造函数:TreeSet(Comparator
祝你好运
是,您可以,但是如上述答案所述,您必须编写一个比较器。
我们取决于强制性的默认自然排序顺序,对象应该是同质的并且是可比较的,否则我们将得到运行时异常,说是类强制转换异常。当且仅当Corresopding类实现可比较的接口时,才称该对象为可比较的。字符串类和所有包装器类已经实现了可比较的接口,但字符串缓冲区类未实现可比较的接口。因此,它将在上面的代码中给出类成本例外。