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

问题描述 投票:1回答:8

我有以下代码,我试图将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
java hashset comparable treeset stringbuffer
8个回答
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());

4
投票
  1. StringBufferpublic final class StringBuffer的事实意味着您不能将其子类化。 StringBuffer非常可变(这很重要,您可以修改缓冲区的内容。)

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

  3. 如果您真的想在TreeSet中使用StringBuffer,则必须提供自己的Comparator,因为StringBuffer不实现Comparable。


2
投票

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

就不变性而言:类声明中的final关键字表示您不能对其进行子类化(扩展)。它本身不会使该类不可变。不可变是指对象的状态一旦创建便无法更改。 StringBuffer创建后肯定可以更改其状态,因此它们不是一成不变的。


1
投票

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

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


0
投票

TreeSet仅采用Comparable对象,而StringBuffer不是Comaprable对象。

TreeSet#add

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);

0
投票

您在问几个问题:

  1. 一般性问题:“您能为哈希设置一个可变的密钥”
  2. 特定问题:“可以将StringBuffer用作TreeSet的键”

您有些困惑,我会帮助您进行整理

[Java中有2种识别策略(或多或少)。)>

  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 比较器)并为StringBuffer定义自己的比较方法

  3. 祝你好运


0
投票

是,您可以,但是如上述答案所述,您必须编写一个比较器。


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.