2014-02-20 46 views
12

我一直在使用Netbeans在Java中開發一段時間,並且有些事情我只是依靠工作而沒有真正質疑如何。其中包括自動生成的hashCode()和equals()方法。爲什麼Netbeans以它的方式生成hashCode()?

equals方法很簡單,但我發現hashCode方法有點神祕。我不明白爲什麼它會選擇乘數並應用它所做的操作。

import java.util.Arrays; 
import java.util.Objects; 

public class Foo { 

    int id; 
    String bar; 
    byte[] things; 

    @Override 
    public int hashCode() { 
     int hash = 7; 
     hash = 89 * hash + this.id; 
     hash = 89 * hash + Objects.hashCode(this.bar); 
     hash = 89 * hash + Arrays.hashCode(this.things); 
     return hash; 
    }  
} 

搜索文檔,本網站和谷歌的東西,如「netbeans生成哈希碼」,似乎沒有什麼相關的。有人在這裏熟悉這一代戰略是什麼,以及爲什麼Netbeans使用它?

編輯:
感謝您的回答!特別是由於this answer on the linked SO question,我理解現在使用primes來設計hashCode方法的邏輯要更完整。然而,至今沒有人真正解決過我的問題的另一個方面,那就是Netbeans如何以及爲何選擇它爲其生成的方法所做的素數。 hash字段和其他乘數(在我的示例中爲89)根據類的各種因素而有所不同。

例如,如果我添加第二個String到類,hashCode()方法變得

public int hashCode() { 
    int hash = 7; 
    hash = 13 * hash + this.id; 
    hash = 13 * hash + Objects.hashCode(this.bar); 
    hash = 13 * hash + Objects.hashCode(this.baz); 
    hash = 13 * hash + Arrays.hashCode(this.things); 
    return hash; 
} 

那麼,爲什麼選擇Netbeans的這些具體素數,而不是任何其他人?

+0

我認爲這只是一種方法,IntelliJ IDEA使用31 –

+0

乘以31很容易優化到一個移位和一個減號,不知道約89.兩者都應該是素數。 – zapl

+2

一個好的選擇是:'返回Objects.hash(id,bar,baz,things);'或多或少都是一樣的東西。 – assylias

回答

3

這是一個旨在更好地散佈散列值的優化。 Eclipse也是這麼做的。看看Why use a prime number in hashCode?Why does Java's hashCode() in String use 31 as a multiplier?

這絕不是必需的。即使return 0;也足以滿足equals/hashcode合約。唯一的原因是基於散列的數據結構在良好的分佈式散列值下表現更好。

有些人會稱之爲過早優化。我認爲這是可以的,因爲它的a)免費(生成)和b)廣泛認可(幾乎每個IDE都可以)。

+0

技術上不需要產生一個好的散列,但'return 0'會導致任何'HashMap'的絕對最壞情況的性能,並且沒有理智的人應該使用這樣一個破碎的散列。我不會稱之爲過早的優化。 – zapl

+0

@zapl我沒有說零是一個很好的散列(「_Even_ ...」),只是這是一個有效的散列。將素數乘以多次以得到完全有效和理智的散列(int,string,byte [])可能被認爲是不成熟的優化,我會支持該語句。 – atamanroman

+1

你是對的,調整素質因素,這很容易過早優化。在return 0之外創建一個基本的實現是IMO不僅僅是優化,它需要使hashCode按照預期工作,如果不是你有點違反合同。 – zapl

2

IBM有an article關於如何編寫自己的equals()hashCode()方法。他們所做的很好,儘管31趨於更好,因爲乘法可以更好地優化。

也看看how String.hashCode() works。就是這樣,但有不同的素數和同質類型。

1

來自Joshua Bloch的第9項,Effective Java 2nd ed。,需要記住的重要一點是,當您重寫equals()以確保相等的對象具有相同的哈希代碼時,總是重寫hashCode(),否則您可能輕易違反此合約。雖然他說,藝術哈希函數的狀態是博士的研究課題,配方,他給出了一個很好的通用的hashCode可能,在你的情況下,產量:

@Override 
public int hashCode() { 
    int result = 17; 
    result = 31 * result + id; 
    result = 31 * result + bar.hashCode(); 
    result = 31 * result + Arrays.hashCode(things); 
    return result ; 
} 

正如@zapl和大衛提到Ehrmann認爲,編譯器可以很容易地將31的乘法優化爲位移和減1操作,所以如果這很重要的話,可能會更快一些。

相關問題