[英]Can object.GetHashCode() produce different results for the same objects (strings) on different machines?
是否有可能同一個對象,尤其是string
或任何原始類型或非常簡單的類型(如struct
),在不同的機器上調用時生成.GetHashCode()
方法的不同值?
例如,表達式"Hello World".GetHashCode()
可以在不同的機器上生成不同的值。 我主要是要求C#.NET,但我想這可能適用於Java甚至其他語言?
編輯:
正如下面的答案和評論所指出的那樣,我知道.GetHashCode()
可以被覆蓋 ,並且不能保證它在不同版本的框架之間產生的結果。 因此,重要的是要澄清我有簡單的類型(不能繼承,因此GetHashCode()
被覆蓋)並且我在所有機器上使用相同版本的框架。
簡短回答:是的。
但簡短的答案並不好玩,是嗎?
當您實現GetHashCode()
您必須做出以下保證:
當在另一個應被視為等於此對象的對象上調用
GetHashCode()
,在此App Domain中將返回相同的值。
而已。 有一些事情你真的需要嘗試做(盡可能多地使用不相等的對象擴散,但不要花太多時間,它首先超過散列的所有好處)和你的代碼如果你不這樣做會很糟糕,但它實際上不會破裂。 如果你不走那么遠就會破裂,因為那樣:
dict[myObj] = 3;
int x = dict[myObj];//KeyNotFoundException
好的。 如果我正在實現GetHashCode()
,為什么我會更進一步,為什么不呢?
首先,為什么我不呢?
也許這是一個稍微不同的程序集版本,我在構建之間改進(或至少嘗試過)。
也許一個是32位,一個是64位,我為了效率而瘋狂,並為每個選擇不同的算法來使用不同的字大小(這不是聞所未聞的,尤其是在散列像集合或字符串這樣的對象時) 。
也許我決定在決定什么構成“平等”對象時要考慮的一些因素本身就是這種方式在不同系統之間變化的。
也許我實際上故意引入一個不同構建的不同種子來捕捉任何同事錯誤依賴我的哈希碼的情況! (我聽說MS使用string.GetHashCode()
的實現來做這件事,但是不記得我是否從可靠或輕信的來源中聽到了這一點。
主要是,這將是前兩個原因之一。
現在,為什么我可以給出這樣的保證?
如果我這么做的話,很可能是偶然的。 如果可以僅基於單個整數id來比較元素的相等性,那么我將使用它作為我的哈希碼。 對於不太好的哈希,任何其他東西都會更有效。 我不太可能改變這個,所以我可能會。
我可能的另一個原因是我自己想要保證。 沒有什么可說的,我不能提供它,只是我不需要。
好的,讓我們做一些實用的事情。 在某些情況下,您可能需要與機器無關的保證。 有些情況下你可能會想要相反的情況,我會稍微談談。
首先,檢查你的邏輯。 你能處理碰撞嗎? 好的,那我們就開始吧。
如果它是你自己的類,那么實現以便提供這樣的保證,記錄它,你就完成了。
如果它不是你的類,那么以提供它的方式實現IEqualityComparer<T>
。 例如:
public class ConsistentGuaranteedComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
return x == y;
}
public int GetHashCode(string obj)
{
if(obj == null)
return 0;
int hash = obj.Length;
for(int i = 0; i != obj.Length; ++i)
hash = (hash << 5) - hash + obj[i];
return hash;
}
}
然后使用它而不是內置的哈希碼。
有一個有趣的案例,我們可能想要相反的情況。 如果我可以控制你正在散列的字符串集,那么我可以選擇一堆具有相同哈希碼的字符串。 你的基於哈希的集合的性能將會變得更糟,並且非常糟糕。 我可以繼續比你處理它更快地做到這一點,所以它可能是一種拒絕服務攻擊。 發生這種情況的情況並不多,但重要的是,如果您正在處理我發送的XML文檔,您不能僅排除某些元素(許多格式允許其中的元素自由)。 然后解析器中的NameTable
會受到傷害。 在這種情況下,我們每次都創建一個新的哈希機制:
public class RandomComparer : IEqualityComparer<string>
{
private int hashSeed = Environment.TickCount;
public bool Equals(string x, string y)
{
return x == y;
}
public int GetHashCode(string obj)
{
if(obj == null)
return 0;
int hash = hashSeed + obj.Length;
for(int i = 0; i != obj.Length; ++i)
hash = hash << 5 - hash + obj[i];
hash += (hash << 15) ^ 0xffffcd7d;
hash ^= (hash >>> 10);
hash += (hash << 3);
hash ^= (hash >>> 6);
hash += (hash << 2) + (hash << 14);
return hash ^ (hash >>> 16)
}
}
這將在給定的使用中保持一致,但從使用到使用不一致,因此攻擊者無法構造輸入以強制它為DoSsed。 順便說一下, NameTable
不使用IEqualityComparer<T>
因為它想要處理具有索引和長度的char數組而不構造字符串,除非必要,但它確實做了類似的事情。
順便說一句,在Java中, string
的哈希碼被指定並且不會改變,但對於其他類可能不是這種情況。
編輯:我已經對上面ConsistentGuaranteedComparer
采用的方法的整體質量進行了一些研究,我不再滿足於在我的答案中使用這些算法; 雖然它用於描述這個概念,但它並沒有像人們想象的那樣好。 當然,如果一個人已經實現了這樣的事情,那么在不破壞保證的情況下就不能改變它,但如果我現在建議使用我的這個庫,那么在研究之后寫的如下:
public class ConsistentGuaranteedComparer : IEqualityComparer<string>
{
public bool Equals(string x, string y)
{
return x == y;
}
public int GetHashCode(string obj)
{
return obj.SpookyHash32();
}
}
對於上面的RandomComparer
,並沒有那么糟糕,但也可以改進:
public class RandomComparer : IEqualityComparer<string>
{
private int hashSeed = Environment.TickCount;
public bool Equals(string x, string y)
{
return x == y;
}
public int GetHashCode(string obj)
{
return obj.SpookyHash32(hashSeed);
}
}
或者更難預測:
public class RandomComparer : IEqualityComparer<string>
{
private long seed0 = Environment.TickCount;
private long seed1 = DateTime.Now.Ticks;
public bool Equals(string x, string y)
{
return x == y;
}
public int GetHashCode(string obj)
{
return obj.SpookyHash128(seed0, seed1).GetHashCode();
}
}
即使在不同的運行中,它也會在同一台機器上產生不同的結果。
所以它基本上可以用來(並且它實際上是用來)在程序的當前運行期間檢查某些東西,但是沒有意義來存儲它,以便在之后檢查它。 導致您獲得的數字是由運行時生成的。
編輯
對於字符串的特定情況,即使在不同的機器上,它也會產生相同的結果,除非機器具有不同的架構。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.