我有以下代码,我试图将StringBuffer对象作为键放在TreeSet中。我这样做的原因是看我是否可以将可变对象作为键。我没有得到任何编译错误。但是当我运行此代码时,我得到的代码下面的错误。
特别是,我得到了java.lang.StringBuffer cannot be cast to java.lang.Comparable
。这个错误表明了什么?
我看到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
答案 0 :(得分:4)
StringBuffer
为public final class StringBuffer
这一事实意味着您无法将其子类化。 StringBuffer非常可变(这就是重点,你可以修改缓冲区的内容。)
你不想使用可变的东西作为键,因为在修改了对象之后,它的equals()和hashcode()方法将返回不同的结果,你将无法找到它再次出现在地图上。
如果你真的想在TreeSet中使用StringBuffer,你必须提供自己的Comparator,因为StringBuffer没有实现Comparable。
答案 1 :(得分:2)
问题在于TreeSet
对您放入其中的项目进行排序。由于StringBuffer
未实现Comparable
,因此TreeSet
不知道如何对其进行排序。您应该在创建Comparator
时传入TreeSet
。您的比较器将告诉TreeSet
如何对StringBuffer
进行排序。要么是这样,要么你可以使用HashSet
,它不对元素进行排序。
就不变性而言:类声明中的final关键字意味着你不能继承(扩展)它。它本身并不能使类不可变。不可变意味着一旦创建了对象的状态就无法更改。 <{1}}确实可以在创建状态后更改状态,因此它们不是不可变的。
答案 2 :(得分:2)
只需添加一个比较器类,然后在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());
答案 3 :(得分:1)
声明类final
并不意味着它是不可变的,这意味着不允许任何类对其进行子类化。事实上,StringBuffer
是非常可变的;这就是班级的重点。
由于StringBuffer
不是Comparable
,您的TreeSet
不知道如何对StringBuffers
进行排序。但是,将可变对象作为任何类型Set
(或Map
)中的键是一个坏主意。如果您必须使用TreeSet
,则创建并使用比较Comparator
个对象的自定义StringBuffer
对象。
答案 4 :(得分:0)
TreeSet
只接受Comparable
个StringBuffer
不是Comaprable
对象的对象。
Throws-ClassCastException - 如果指定的对象无法与此set中当前的元素进行比较。
您可以使用String
对象(因为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);
答案 5 :(得分:0)
你问了几个问题:
你有些困惑,我会帮你解决它们
Java中使用了两种识别策略(或多或少)。
哈希:输入“Foo”被转换为尽可能最好的尝试,以生成唯一访问数组索引的数字。 (纯粹主义者,请不要虐待我,我故意简化)。此索引是存储值的位置。 “Foo”和“Bar”实际上可能生成相同的索引值,这意味着它们都将映射到相同的数组位置。显然这不起作用,所以这就是“equals()”方法的用武之地;它用于消除歧义
比较:通过使用比较方法,您不需要这个额外的消歧步骤,因为比较从不首先产生此碰撞。 “Foo”等于的唯一关键是“Foo”。一个非常好的想法是,如果你可以将“equals()”定义为compareTo()== 0;为了一致的缘故。不是要求。
现在提出一般问题:
地图的密钥是否可以变更。答:是的,非常非常糟糕和愚蠢。示例:Map.put(k,v); k.modifyInternalHash(); Map.get(k)= null; //坏了这里 实际上,这是通过散乱的疏忽而发生的。虽然比较地图可能会出现这种情况但诊断问题会更容易。
StringBuffer可以用作TreeMap / Set的键吗?是。使用替代构造函数:TreeSet(Comparator&lt; T&gt;比较器)并为StringBuffer定义自己的比较方法
答案 6 :(得分:0)
是的,你可以,但正如上面的答案所述,你必须写一个比较器。
但真正的问题是你为什么要这样做? StringBuffer的目的是在创建字符串时修改状态。由于它是SortedMap中的一个键,因此不应该修改键,因此保存StringBuffer没有意义。你想要做的是调用StringBuffer.toString(),它返回一个String并使用String作为你的键。
答案 7 :(得分:0)
我们取决于强制性的默认自然排序顺序,对象应该是同质的并且是可比的,否则我们将获得运行时异常,即类强制转换异常。 当且仅当Corresopding类实现可比较的接口时,才称该对象为可比较的。 字符串类和所有包装器类已经实现了可比较的接口,但字符串缓冲区类未实现可比较的接口。 因此,它将在上面的代码中给出类成本例外。