可以将StringBuffer对象作为Java中TreeSet中的键吗?

我有以下代码,我试图将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 sb=new TreeSet(); 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 { @Override public int compare(StringBuffer s1, StringBuffer s2) { return s1.toString().compareTo(s2.toString()); } } in your main method: modify as follows Set sb=new TreeSet(new Comparatorbuff()); 
  1. StringBufferpublic final class StringBuffer的事实意味着你不能将它子类化。 StringBuffer非常可变(这就是重点,你可以修改缓冲区的内容。)

  2. 你不想使用可变的东西作为键,因为在修改了对象之后,它的equals()和hashcode()方法将返回不同的结果,你将无法在Map中找到它。

  3. 如果你真的想在TreeSet中使用StringBuffer,那么你必须提供自己的Comparator,因为StringBuffer没有实现Comparable。

问题是TreeSet您放入的项目进行排序。 因为StringBuffer不实现Comparable ,所以TreeSet不知道如何对它们进行排序。 您应该在创建TreeSet时传入Comparator 。 比较器将告诉TreeSet如何对StringBuffer进行排序。 或者,或者您可以使用HashSet ,它不对元素进行排序。

就不变性而言:类声明中的final关键字意味着你不能inheritance它(扩展)它。 它本身并不能使类不可变。 不可变意味着一旦创建了对象的状态就无法更改。 StringBuffer绝对可以在创建后更改状态,因此它们不是不可变的。

声明一个类final并不意味着它是不可变的,这意味着不允许任何类对它进行子类化。 事实上, StringBuffer非常可变; 这就是class级的重点。

因为StringBuffer不是Comparable ,所以TreeSet不知道如何对StringBuffers进行排序。 但是,将可变对象作为任何类型的Set (或Map )中的键是一个坏主意。 如果必须使用TreeSet ,则创建并使用比较StringBuffer对象的自定义Comparator对象。

TreeSet只接受Comparable对象,而StringBuffer不是Comaprable对象。

TreeSet中添加#

Throws-ClassCastException – 如果指定的对象无法与此set中当前的元素进行比较。

您可以使用String对象(因为String是Comparable)而不是StringBuffer对象。
例如:

  Set sb=new TreeSet(); 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); 

你问了几个问题:

  1. 一般问题:“你有一个哈希的可变密钥”
  2. 具体问题:“可以将StringBuffer用作TreeSet的键”

你有些困惑,我会帮助你解决它们

Java中的地图使用了2种识别策略(或多或少)。

  1. 散列:输入“Foo”被转换为尽可能最好的尝试,以生成唯一访问数组索引的数字。 (纯粹主义者,请不要虐待我,我故意简化)。 此索引是存储值的位置。 “Foo”和“Bar”实际上可能生成相同的索引值,这意味着它们都将映射到相同的数组位置。 显然这不起作用,所以这就是“equals()”方法的用武之地; 它用于消除歧义

  2. 比较:通过使用比较方法,您不需要这个额外的消歧步骤,因为比较首先不会产生这种碰撞。 “Foo”等于的唯一关键是“Foo”。 一个非常好的想法是,如果你可以将“equals()”定义为compareTo()== 0; 为了一致性。 不是要求。

现在问你的一般问题:

  1. 地图的关键字是否可变。 答:是的,非常非常糟糕和愚蠢。 示例:Map.put(k,v); k.modifyInternalHash(); Map.get(k)= null; //在这里不好
    实际上,这是通过散乱的疏忽而发生的。 虽然比较地图可能会出现这种情况,但对于诊断来说这将是一个更容易的问题。

  2. StringBuffer可以用作TreeMap / Set的键吗? 是。 使用替代构造函数:TreeSet(Comparator 比较器)并为StringBuffer定义自己的比较方法

祝好运

是的,你可以,但正如上面的答案所述,你必须写一个比较器。

但真正的问题是你为什么要这样做? StringBuffer的目的是在创建字符串时修改状态。 由于它是SortedMap中的一个键,因此不应该修改键,因此保存StringBuffer没有意义。 你想要做的是调用StringBuffer.toString(),它返回一个String并使用String作为你的键。