VB.NET GetHashCodeの実装を考える

Javaの基本的な実装はこんな感じ。
(null考慮とか周辺部はちょっと割愛)

public int hashCode() {
	int hash = 1;
	hash = hash * 31 + f1.hashCode();
	hash = hash * 31 + f2.hashCode();
	return hash;
}

[Efective Java]とかに載っている例、Eclipseで自動生成させたときもこんな感じになる。

これをVB.NETでそのままやると
"* 31"でオーバーフローorz

int(Integer)の上限値は同じなのだけど、プリミティブ型のGetHashCodeが大きな値を返すのね...

そもそも、"* 31"とかってのは順序性の重み付けのため。
たとえば、Vectorなんか(その名のとおり)[1,2,3]と[3,1,2]を同一してはいけないから。


でもでも、オブジェクトの等価性(equals/hashCode)ってのは「要件による」ってのが一般論。


たとえばこんなクラスがあったとして

class MailAddress {
	String mailAddres1;
	String mailAddres2;
	String mailAddres3;
}

「mailAddres1〜3は入力画面でフィールドが3つならんでおり、ユーザはどこでも入力できること」っていう要件があったならば、
このクラスのhashCodeは重み付けしちゃいけない。


とはいえ、そもそも「!y.equals(x)」の時「y.hashCode() == x.hashCode()」であっても構わない言語仕様なので、
極端な話、「int hushCode() { return 0;}」でもいい(前部equalsに当たりにいくのでHashMapのコストが超高くなるけど)。
# 他の後発言語も同じ事情のはず。



ところで...

VB.NETの基本的な実装はこんな感じ
(null考慮とか周辺部はちょっと割愛)

Public Overrides Function GetHashCode() As Integer
	Dim hashCode As Integer = 0
	hashCode = hashCode Xor f1.GetHashCode()
	hashCode = hashCode Xor f2.GetHashCode()
	Return hashCode
End Function

MSDNVB.NET以外のサンプルがある。
ちなみに、ReSharperでジェネレートするコードもこんな感じ。

さて、ここで問題がある。

重み付はどうしよっか?


もちろん、このテストケースは失敗する。

Assert.AreNotEqual("あ".GetHashCode Xor "い".GetHashCode Xor "う".GetHashCode, "う".GetHashCode Xor "あ".GetHashCode Xor "い".GetHashCode)

掛け算がだめなら、"+ 1"でもしとくとかな?
# 完全な解決にはならないのがhashCode...

MailAddressの例はあんまりだけど、配列やList、Dictionaryのフィルードがあれば、ちょっと引っかかると思うのだけど
.Neterなみなさん、どうしてます?




log4netはあるのに、Commons Langはないのは何故だろう。

EqualsBuilderとか、HashCodeBuilderとか、ToStringBuilderがあればとりあえずそれを使うのだけど、ニーズがないのかしらん?

# 別に、Javaみたくガチでオブシコに取組たい分けじゃないんだけどwww


Cache機構を作る場合、DictionaryのキーとなるImplements IEquatableなクラスが必要になるから^^;



ちなみに、ReSharperでジェネレートしたコードもこんな感じ。

Class Person
        Public FirstName As String
        Public LastName As String

        Public Overloads Function Equals (ByVal other As Person) as Boolean
            If ReferenceEquals (Nothing, other) Then Return False
            if ReferenceEquals (Me, other) Then Return True
            Return Equals (other.FirstName, FirstName) AndAlso Equals (other.LastName, LastName)

        End Function

        Public Overloads Overrides Function Equals (ByVal obj As Object) as Boolean
            If ReferenceEquals (Nothing, obj) Then Return False
            if ReferenceEquals (Me, obj) Then Return True
            If Not Equals (obj.GetType(), GetType (Person)) Then Return False
            Return Equals (DirectCast (obj, Person))

        End Function

        Public Overrides Function GetHashCode() as Integer
            Dim hashCode as Integer = 0
            If FirstName IsNot Nothing Then hashCode = (hashCode*397) Xor FirstName.GetHashCode()
            If LastName IsNot Nothing Then hashCode = (hashCode*397) Xor LastName.GetHashCode()
            Return hashCode
        End Function
    End Class


オーバーフロー