[英]How to override Equals and GetHash of HashSet
我有一個HashSet<int[]> foo
其中 int[] 代表平面中一個點的坐標。 位置 0 處的值表示x ,位置 1 處的值表示y 。 我想覆蓋 Equals 和 GetHashCode 方法,以便能夠刪除一個元素(表示為大小為 2 的數組的點),如果其內部值等於給定的值。
已經嘗試過:
public override int GetHashCode(){
return this.GetHashCode();
}
public override bool Equals(object obj){
if (obj == null || ! (obj is int[]))
return false;
HashSet<int[]> item = obj as HashSet<int[]>;
return item == this;
}
在我的課堂迷宮。
提前致謝。
編輯
我找到了一種方法來做到這一點
class SameHash : EqualityComparer<int[]>
{
public override bool Equals(int[] i1, int[] i2)
{
return i1[0] == i2[0] && i1[1] == i2[1];
}
public override int GetHashCode(int[] i)
{
return base.GetHashCode();
}
}
看起來您似乎解決了您所要求的問題,但有一些重要的事情需要指出。 當您實現EqualityComparer<int[]>
您將GetHashCode(int[] i)
編碼為return base.GetHashCode();
即使它有效,這也是不正確的。 我花時間為您提供了下面的代碼,以便您查看您的實現結果,並且我還為您提供了一個可能的解決方案。 復制此代碼並在控制台項目中運行它。 注釋您的代碼行,取消注釋其正下方的行並再次運行它。 你會看到不同! 總而言之,當您返回base.GetHashCode()
您將為每個項目返回相同的哈希碼。 這會導致所有插入的哈希集內部發生沖突,最終行為就像您使用List<int[]>
一樣慢,並且您在插入之前詢問它是否包含元素。 這就是為什么您會看到,通過使用我提供給您的函數以及我生成的數字范圍,您將能夠在不到 1 秒的時間內插入多達一百萬次。 然而,使用你的,無論范圍如何,在大約一萬次插入中花費 1 秒。 發生這種情況是因為對於所有 n 次插入都存在沖突,並且當 HashSet 和均勻分布的哈希函數的預期為 O(n) 時,由此產生的時間復雜度為 O(n^2)。 看一下這個:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace hashExample
{
class Program
{
static void Main(string[] args)
{
List<int[]> points = new List<int[]>();
Random random = new Random();
int toInsert = 20000;
for (int i = 0; i < toInsert; i++)
{
int x = random.Next(1000);
int y = random.Next(1000);
points.Add(new int[]{ x,y });
}
HashSet<int[]> set = new HashSet<int[]>(new SameHash());
Stopwatch clock = new Stopwatch();
clock.Start();
foreach (var item in points)
{
set.Add(item);
}
clock.Stop();
Console.WriteLine("Elements inserted: " + set.Count + "/" + toInsert);
Console.WriteLine("Time taken: " + clock.ElapsedMilliseconds);
}
public class SameHash : EqualityComparer<int[]>
{
public override bool Equals(int[] p1, int[] p2)
{
return p1[0] == p2[0] && p1[1] == p2[1];
}
public override int GetHashCode(int[] i)
{
return base.GetHashCode();
//return i[0] * 10000 + i[1];
//Notice that this is a very basic implementation of a HashCode function
}
}
}
}
我發現它可能的唯一方法是創建一個類 MyPair 而不是像你那樣使用數組 (int[])。 請注意,我在 GetHashCode() 函數中使用了 X*10000 + Y,但您可以更改常量值以便為每個項目獲得更好的 HashCode,或者您可以創建自己的。 我只是提供這個作為一個簡單的例子,因為當 X 和 Y 的邊界相對較小(小於 Int.MaxValue 的根)時,這是一種使用不同 hashCode 的簡單方法。 在這里你有工作代碼:
using System;
using System.Collections.Generic;
using System.Linq;
namespace hash
{
public class MyPair
{
public int X { get; set; }
public int Y { get; set; }
public override int GetHashCode()
{
return X * 10000 + Y;
}
public override bool Equals(object obj)
{
MyPair other = obj as MyPair;
return X == other.X && Y == other.Y;
}
}
class Program
{
static void Main(string[] args)
{
HashSet<MyPair> hash = new HashSet<MyPair>();
MyPair one = new MyPair { X = 10, Y = 2 };
MyPair two = new MyPair { X = 1, Y = 24 };
MyPair three = new MyPair { X = 111, Y = 266 };
MyPair copyOfOne = new MyPair { X = 10, Y = 2 };
Console.WriteLine(hash.Add(one));
Console.WriteLine(hash.Add(two));
Console.WriteLine(hash.Add(three));
Console.WriteLine(hash.Add(copyOfOne));
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.