2014-04-24 41 views
1

包含HashMap和哈希碼質疑哈希映射和哈希碼的變化,如何判斷對象已經改變?

有一個POJO,過書面哈希碼,等於幫助一個partcular比較(這裏沒有顯示)

package coll.hset; 

public class Dat { 

    private String name; 
    private String dat; 
    private int aa;//some business reason not used in hashcode and equals 

    public int hashCode(){ 
     int h = 0 ; 
     if(name != null){ 
      h += name.hashCode(); 
     } 
     if(dat != null){ 
      h += dat.hashCode(); 
     } 
     return h; 
    } 

    public boolean equals(Object o){ 
     if(o instanceof Dat){ 
      Dat oo = (Dat)o; 
      if(this.name ==null && oo.name != null){ 
       return false; 
      }else if(!name.equals(oo.name)){ 
       return false; 
      } 

      if(this.dat ==null && oo.dat != null){ 
       return false; 
      }else if(!dat.equals(oo.dat)){ 
       return false; 
      } 
      return true; 
     } 
     return false; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public String getDat() { 
     return dat; 
    } 

    public void setDat(String dat) { 
     this.dat = dat; 
    } 

    public int getAa() { 
     return aa; 
    } 

    public void setAa(int aa) { 
     this.aa = aa; 
    } 

} 

的用戶應用程序:

package coll.hset; 

import java.util.HashSet; 
import java.util.Random; 

public class App { 

    final static int SZ = 2^8; 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     Random rndm = new Random();// to create random data 
     Dat dd;// reference while filling up set 
     Dat[] d2 = new Dat[500];// save a few here for later ops 
     int fills = 0; 
     HashSet<Dat> dats = new HashSet<Dat>();// set 
     for (int i = 0; i < 10000; i++) { 
      dd = new Dat(); 
      dd.setAa(i); 
      // fill random dat and name. 
      char v = (char) (65 + rndm.nextInt(26)); 
      dd.setDat("a " + v); 
      v = (char) (65 + rndm.nextInt(26)); 
      char v1 = (char) (65 + rndm.nextInt(26)); 
      char v2 = (char) (65 + rndm.nextInt(26)); 
      char v3 = (char) (65 + rndm.nextInt(26)); 
      char v4 = (char) (65 + rndm.nextInt(26)); 
      dd.setName(v + " " + v1 + v2 + v3 + v1 + v + v4); 
      dats.add(dd); 
      if (i % 60 == 0) { 
       d2[fills++] = dd; 
      } 

     } 
     Dat ref = d2[0]; 
     int hash = hash(ref.hashCode()); 
     int idx = indexFor(hash, SZ); 
     boolean has1 = dats.contains(d2[0]); 
     System.out.println("has d 0 :" + has1 + ", name :" + ref.getName() + ", hash :" + ref.hashCode() + ". hash2 :" + hash + ", idx :" + idx + ", when size of table :" + SZ); 

     d2[0].setName(ref.getName() + "l"); 
     // d2[0].setName(ref.getName() + "l"); 
     d2[0].setName("Tony G"); 
     // ref.setDat("sd="); 
     hash = hash(ref.hashCode()); 
     // if you run this many times will see that for some cases the table is the same, so a quicker rehash, instead of remove and add back after change is what I'm after 
     idx = indexFor(hash, SZ); 
     has1 = dats.contains(d2[0]); 
     System.out.println("has d 0 after name change :" + has1 + ", name :" + ref.getName() + "."); 
     System.out.println("has d 0 :" + has1 + ", name :" + ref.getName() + ", hash :" + ref.hashCode() + ". hash2 :" + hash + ", idx :" + idx + ", when size of table :" + SZ); 
     System.out.println(" at : " + new java.util.Date()); 

    } 

    static int hash(int h) { 
     // From Sun Java impl 
     /* 
     */This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). 
     */ 
     h ^= (h >>> 20)^(h >>> 12); 

     return h^(h >>> 7)^(h >>> 4); 

    } 

    static int indexFor(int h, int length) { 
     return h & (length - 1); 
    } 
} 

由於預期第二次搜索說出來Dat對象d2 [0]即使認爲它不在集合中。 我知道如何解決它 - 一種方法 - 刪除它,改變它然後再添加它。有沒有其他辦法可以告訴我們我們正在改變一個特定的對象?

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/HashMap.java#HashMap.remove%28java.lang.Object%29

能看到甲骨文/ Sun的Java HashMap中怎麼老調重彈它的自我。問題是 - 我們可以添加一個告訴集合的新方法 - 請重新哈希這個對象,而不是刪除和添加它,所以它更有效。

如果你運行上面的代碼很多次會看到,某些情況下,表是相同的(在之前和之後突變對象的散列碼),所以更快的翻版,而不是刪除和添加回來後的變化是什麼我之後,利用了這個事實,只有在換桶的時候纔會反覆。

+0

由於您的非說話變量,代碼非常難以閱讀。例如。如果'rr'的名字是'random',那會更好。 –

+1

簡單:_Don't改變它!_ – Seelenvirtuose

+0

所以告訴我們的商業用戶 - 嘿,請不要改變你的名字或目前的地址?我認爲不是 – tgkprog

回答

4

在對象的生命週期中假定對象的哈希值是不變的,所以嚴格回答你的問題是:不。當你修改你的對象使得它的哈希代碼被改變時,你最好從地圖上移除它並重新添加它。

+0

正確的我在自己的問題中說過很多,想知道是否有更快的方式,以便更大的集合,它可以花更少的時間去除(重新哈希) – tgkprog

+2

我不認爲有更快的方法。正如散列表數據結構所暗示的,已更改的散列需要將對象從一個存儲桶移到另一個存儲桶。這樣的舉動實際上是重新添加對象到地圖。 –

1

只要在執行Java應用程序時多次調用同一對象的hashcode()函數,hashCode方法必須一致地返回相同的整數,前提是在對象的等值比較中未使用任何信息被修改。這個整數不需要從應用程序的一次執行到同一個應用程序的另一次執行保持一致。所以@kiril建議將它從map中移除並再次將其添加回去。