2013-08-22 21 views
1

我有下面的代碼,我試圖把StringBuffer對象作爲鍵在TreeSet中。我這樣做的原因是看我是否可以將可變對象作爲關鍵字。我沒有收到任何編譯錯誤。但是當我運行這段代碼時,我得到了代碼下面的錯誤。 特別,我得到這個java.lang.StringBuffer cannot be cast to java.lang.Comparable。這個錯誤表明什麼?我看到StringBuffer類聲明爲final(public final class StringBuffer),這不是說它是不可變的,因此可以哈希?可以StringBuffer對象是Java中TreeSet中的鍵嗎?

我是哈希和不可變的東西的新手,所以請在這裏幫助我。

感謝

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 

回答

1

只需添加一個比較器類,然後在你的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()); 
+0

如果他修改StringBuffer並搜索它的舊值或它新值將永遠不會被找到,因爲二進制搜索將索引到compareTo永遠不會= 0的位置;除了特殊情況下,更改仍將值放在相同的樹相對位置。 海報底線。不要改變密鑰。非常非常糟糕。你基本上失去了數據 –

2

的問題是,TreeSet各種各樣你把它的項目。由於StringBuffer未執行Comparable,因此TreeSet不知道如何對它們進行排序。當您創建TreeSet時,您應該輸入Comparator。您的比較器會告訴TreeSet如何對StringBuffer進行排序。要麼是這樣,要麼您可以使用不排序元素的HashSet

就不變性而言:類聲明的final關鍵字意味着您不能繼承它(繼承)。它本身並不能使這個類不可變。不可變意味着對象的狀態一旦創建就無法更改。 StringBuffer確實可以在創建後改變它們的狀態,所以它們不是一成不變的。

1

聲明一個類final並不意味着它是不可變的,這意味着沒有類可以繼承它的子類。其實StringBuffer是非常可變的;這是班上的重點。

因爲StringBuffer不是Comparable,您的TreeSet不知道如何對StringBuffers進行排序。但是,在任何種類的Set(或Map)中都有可變對象是一個關鍵字是個不錯的主意。如果您必須使用TreeSet,則創建並使用自定義Comparator對象,該對象將比較StringBuffer對象。

4
  1. StringBufferpublic final class StringBuffer這一事實意味着您不能繼承它。 StringBuffer是非常易變的(就這一點而言,你可以修改緩衝區的內容)。

  2. 你不想使用可變的鍵作爲鍵,因爲在對象被修改之後,它的equals()和hashcode()方法將返回不同的結果,您將無法在Map中找到它。

  3. 如果你真的想在TreeSet中使用StringBuffer,你將不得不提供你自己的比較器,因爲StringBuffer沒有實現Comparable。

0

TreeSet僅需Comparable對象,其中作爲StringBufferComaprable對象。

TreeSet#add

拋出-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的一個鍵「

你有些困惑,我會幫你把它們整理出來

有2種用於Java地圖(或多或少)的識別策略。

  1. 散列:一個輸入「foo」被轉換成一個最佳-AS-可能嘗試以產生數字,它唯一訪問的索引到一個數組。 (純粹主義者,請不要濫用我,我故意簡化)。該索引是您的值存儲的位置。 「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的(比較< T>比較),並定義爲StringBuffer的

祝你自己的比較方法

+0

這是否意味着,如果我實現Comparable接口,那麼我不必有平等和哈希方法無效,我仍然可以使用我的對象在HashMap中? – eagertoLearn

+0

不可以。如果您的對象實現了Comparable,那麼它意味着使用TreeSet/TreeMap或任何其他Sorted Set/Map。 public int hashCode()和public boolean equals(Object o)專門用於哈希集合。所以,備忘單: 使用HashSet或HashMap:必須實現hashCode()和equals() 使用TreeSet或TreeMap:必須實現Comparable或Comparator並傳遞給構造函數 –

0

是的,你可以,但如上述答案的狀態,你必須寫一個比較。

但真正的問題是你爲什麼要? StringBuffer的目的是在創建字符串時修改狀態。由於它是SortedMap中的一個鍵,所以不應該修改鍵,所以保存StringBuffer沒有意義。你想要做的就是調用StringBuffer.toString(),它返回一個String並使用String作爲你的鍵。

相關問題