StringBuffer对象可以是Java中TreeSet中的键吗?

时间:2013-08-22 19:33:14

标签: java hashset comparable treeset stringbuffer

我有以下代码,我试图将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

8 个答案:

答案 0 :(得分:4)

  1. StringBufferpublic final class StringBuffer这一事实意味着您无法将其子类化。 StringBuffer非常可变(这就是重点,你可以修改缓冲区的内容。)

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

  3. 如果你真的想在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只接受ComparableStringBuffer不是Comaprable对象的对象。

TreeSet#add

  

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)

你问了几个问题:

  1. 一般问题:“你有一个哈希的可变密钥”
  2. 具体问题:“可以将StringBuffer用作TreeSet的键”
  3. 你有些困惑,我会帮你解决它们

    Java中使用了两种识别策略(或多或少)。

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

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

    3. 现在提出一般问题:

      1. 地图的密钥是否可以变更。答:是的,非常非常糟糕和愚蠢。示例:Map.put(k,v); k.modifyInternalHash(); Map.get(k)= null; //坏了这里 实际上,这是通过散乱的疏忽而发生的。虽然比较地图可能会出现这种情况但诊断问题会更容易。

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

      3. 祝你好运

答案 6 :(得分:0)

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

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

答案 7 :(得分:0)

我们取决于强制性的默认自然排序顺序,对象应该是同质的并且是可比的,否则我们将获得运行时异常,即类强制转换异常。 当且仅当Corresopding类实现可比较的接口时,才称该对象为可比较的。 字符串类和所有包装器类已经实现了可比较的接口,但字符串缓冲区类未实现可比较的接口。 因此,它将在上面的代码中给出类成本例外。