2011-11-30 61 views
5

我正在嘗試使用Eclipse將條目添加到Java中的哈希表。在投入操作期間,只有一個密鑰被新的密鑰和值覆蓋。散列表的計數保持正確,但其中一個(鍵,值)對丟失。Java Hashtable在'put'過程中用新密鑰覆蓋現有密鑰

這是我的示例代碼:

ArrayList<Double> list; 
Hashtable<Val,ArrayList<Double>> numbers = new Hashtable<Val,ArrayList<Double>>(); 

while((line = brMyHashval.readLine()) != null) 
{ 
    if(!(line.isEmpty())) 
    {    
     String[] temp; 
     temp = line.split(" ");  
     eDouble = Double.parseDouble(temp[2].toString()); 

     Val key = new Val(Double.parseDouble(temp[0].toString()) ,Double.parseDouble(temp[1].toString())); 

     if(!(numbers.containsKey(key))) 
     { 
      list = new ArrayList<Double>(); 
      numbers.put(key, list); 

     } 
     else 
     { 
      list = numbers.get(key); 
     } 
     list.add(eDouble); 
    } 
} 

我已經使用到內置「哈希碼」和「等於」方法在蝕用於比較類對象。

輸入文本文件:

1.0 2.0 9.0 
3.0 4.0 9.0 
5.0 6.0 9.0 
1.0 2.0 8.0 
5.0 6.0 8.0 
1.0 2.0 7.0 
**7.0 8.0 7.0** // After this point a new hash entry gets added for key(7,8), But key (1,2) get deleted from the hashtable, though count gets increased to 4. 
3.0 4.0 7.0 
5.0 6.0 10.0 
1.0 2.0 10.0 
1.0 3.0 10.0 
1.0 4.0 10.0 

爲什麼關鍵獲取該特定即時刪除。?

[編輯]哈希碼和等於:我用蝕自動導入這些方法 //(X,Y)是(A,B)

class Val 

{ 
    double x; 
    double y; 

Val(double X, double Y) 
{ 
    x = X; 
    y = Y; 
} 

@Override 
public int hashCode() { 
    final int prime = 31; 
    int result = 1; 
    long temp; 
    temp = Double.doubleToLongBits(x); 
    result = prime * result + (int) (temp^(temp >>> 32)); 
    temp = Double.doubleToLongBits(y); 
    result = prime * result + (int) (temp^(temp >>> 32)); 
    return result; 
} 

@Override 
public boolean equals(Object obj) { 
    if (this == obj) 
     return true; 
    if (obj == null) 
     return false; 
    if (getClass() != obj.getClass()) 
     return false; 
    Val other = (Val) obj; 
    if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) 
     return false; 
    if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) 
     return false; 
    return true; 
} 

}

+2

什麼是你的hashCode和equals呢? – Thilo

+0

什麼是「Val」班?它與「K」相同嗎? – dmeister

+0

是的。抱歉。 Val類是K類 – SyncMaster

回答

6

問題是您正在使用調試器來檢查HashMap的內容。

我假設鍵(1,2)和(7,8)都保存在用於保存鍵的HashTable的相同槽中。作爲(7,8)得到補充,(1,2)移動到的(7,8)「回」 - 你必須檢查(7,8)項的next條目。

enter image description here

添加以下代碼的看看到底什麼是真正的HashMap中:

for (Val key : numbers.keySet()) { 
     System.out.printf("%.1f %.1f: %s%n", key.x, key.y, numbers.get(key)); 
    } 
+1

使用調試器檢查HashMap的好處可能並不直觀。 –

1

確保散列和等於滿足他們的要求。

每個實例都應該有一個唯一的散列,如果它們只相等,它們應該是true。假陽性意味着假陽性值映射到同一個鍵。 See this link.

2

上面的Sumindra表示您想要將自定義類用作Map中的鍵,您必須按指定的方式寫入equals()和hashCode()方法。做下面的(例如:

public boolean equals(K other) { 
    return a == other.a && b == other.b; 
} 

public int hashCode() { 
    return new Double(a).hashCode()^new Double(b).hashCode(); 
} 

這保證:

  • 2 K個對象retuyrn相等如果他們有相同的成員
  • 2 K個對象有相同的hashCode,如果他們有相同的成員

這是地圖的關鍵對象的要求。

+0

即使你提到的代碼,同樣的問題發生。即對於不同的成員,兩個K對象返回相等。 我應該以不同的方式寫入方法嗎?我找不到任何理由使用它,即使給出相同的值不同成員^ – SyncMaster

+1

@SyncMaster提防兩個對象可以返回相同的'hashCode',仍然是不同的!例如,字符串的位數可能比散列碼多得多,因此不可能爲每個字符串提供獨特的代碼。在你的情況下,兩個雙打有更多的位長比(哈希碼返回類型) –

+0

@CarlosHeuberger:所以更好的方式來處理這樣的情況下寫我自己合適的哈希碼? – SyncMaster

1

我無法重現您的問題,這是我正在運行的EXACT代碼(不是簡單的,因爲其他答案儘可能接近您的原始問題)。

public class HashProblem { 

    public static class Val { 
     private double x; 
     private double y; 

     public Val(double x, double y) { 
      this.x = x; 
      this.y = y; 
     } 

     @Override 
     public int hashCode() { 
      final int prime = 31; 
      int result = 1; 
      long temp; 
      temp = Double.doubleToLongBits(x); 
      result = prime * result + (int) (temp^(temp >>> 32)); 
      temp = Double.doubleToLongBits(y); 
      result = prime * result + (int) (temp^(temp >>> 32)); 
      return result; 
     } 

     @Override 
     public boolean equals(Object obj) { 
      if (this == obj) 
       return true; 
      if (obj == null) 
       return false; 
      if (getClass() != obj.getClass()) 
       return false; 
      Val other = (Val) obj; 
      if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) 
       return false; 
      if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) 
       return false; 
      return true; 
     } 
    } 

    public static void main(String... args) throws Exception { 
     ArrayList<Double> list; 
     String line; 
     BufferedReader brMyHashval = new BufferedReader(new InputStreamReader(new FileInputStream("HashProblem.txt"))); 
     Hashtable<Val, ArrayList<Double>> numbers = new Hashtable<Val, ArrayList<Double>>(); 

     while ((line = brMyHashval.readLine()) != null) { 
      if (!(line.isEmpty())) { 
       String[] temp; 
       temp = line.split(" "); 
       Double eDouble = Double.parseDouble(temp[2].toString()); 

       Val key = new Val(Double.parseDouble(temp[0].toString()), Double.parseDouble(temp[1].toString())); 

       if (!(numbers.containsKey(key))) { 
        list = new ArrayList<Double>(); 
        numbers.put(key, list); 
        System.err.println("Created " + key.x + " " + key.y); 
       } else { 
        list = numbers.get(key); 
       } 
       list.add(eDouble); 
       System.err.println("Inserted into " + key.x + " " + key.y + " value " + eDouble + " size " + list.size() + " " + list); 
      } 
     } 
    } 

我從日誌中得到的輸出是

Created 1.0 2.0 
Inserted into 1.0 2.0 value 9.0 size 1 [9.0] 
Created 3.0 4.0 
Inserted into 3.0 4.0 value 9.0 size 1 [9.0] 
Created 5.0 6.0 
Inserted into 5.0 6.0 value 9.0 size 1 [9.0] 
Inserted into 1.0 2.0 value 8.0 size 2 [9.0, 8.0] 
Inserted into 5.0 6.0 value 8.0 size 2 [9.0, 8.0] 
Inserted into 1.0 2.0 value 7.0 size 3 [9.0, 8.0, 7.0] 
Created 7.0 8.0 
Inserted into 7.0 8.0 value 7.0 size 1 [7.0] 
Inserted into 3.0 4.0 value 7.0 size 2 [9.0, 7.0] 
Inserted into 5.0 6.0 value 10.0 size 3 [9.0, 8.0, 10.0] 
Inserted into 1.0 2.0 value 10.0 size 4 [9.0, 8.0, 7.0, 10.0] 
Created 1.0 3.0 
Inserted into 1.0 3.0 value 10.0 size 1 [10.0] 
Created 1.0 4.0 
Inserted into 1.0 4.0 value 10.0 size 1 [10.0] 

不是你所期待什麼呢?

其他答案有關於簡化您的hashCode和等於好點。另外,你不需要在已經是字符串的對象上做toString()。