C# HashSet 如何比较元素是否相等?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8952003/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does HashSet compare elements for equality?
提问by nima
I have a class that is IComparable:
我有一个类是IComparable:
public class a : IComparable
{
public int Id { get; set; }
public string Name { get; set; }
public a(int id)
{
this.Id = id;
}
public int CompareTo(object obj)
{
return this.Id.CompareTo(((a)obj).Id);
}
}
When I add a list of object of this class to a hash set:
当我将此类的对象列表添加到哈希集时:
a a1 = new a(1);
a a2 = new a(2);
HashSet<a> ha = new HashSet<a>();
ha.add(a1);
ha.add(a2);
ha.add(a1);
Everything is fine and ha.countis 2, but:
一切都很好,ha.count是2,但是:
a a1 = new a(1);
a a2 = new a(2);
HashSet<a> ha = new HashSet<a>();
ha.add(a1);
ha.add(a2);
ha.add(new a(1));
Now ha.countis 3.
现在ha.count是3。
- Why doesn't
HashSetrespecta'sCompareTomethod. - Is
HashSetthe best way to have a list of unique objects?
- 为什么不
HashSet尊重a的CompareTo方法。 - 是
HashSet拥有唯一对象列表的最佳方法吗?
采纳答案by Jon Skeet
It uses an IEqualityComparer<T>(EqualityComparer<T>.Defaultunless you specify a different one on construction).
它使用IEqualityComparer<T>(EqualityComparer<T>.Default除非您在构造时指定不同的)。
When you add an element to the set, it will find the hash code using IEqualityComparer<T>.GetHashCode, and store both the hash code and the element (after checking whether the element is already in the set, of course).
当你向集合中添加一个元素时,它会使用 找到哈希码IEqualityComparer<T>.GetHashCode,并存储哈希码和元素(当然是在检查元素是否已经在集合中之后)。
To look an element up, it will first use the IEqualityComparer<T>.GetHashCodeto find the hash code, then for all elements with the same hash code, it will use IEqualityComparer<T>.Equalsto compare for actual equality.
要查找元素,它将首先使用IEqualityComparer<T>.GetHashCode来查找哈希码,然后对于具有相同哈希码的所有元素,它将IEqualityComparer<T>.Equals用于比较实际相等性。
That means you have two options:
这意味着您有两个选择:
- Pass a custom
IEqualityComparer<T>into the constructor. This is the best option if you can't modify theTitself, or if you want a non-default equality relation (e.g. "all users with a negative user ID are considered equal"). This is almost never implemented on the type itself (i.e.Foodoesn't implementIEqualityComparer<Foo>) but in a separate type which is only used for comparisons. - Implement equality in the type itself, by overriding
GetHashCodeandEquals(object). Ideally, implementIEquatable<T>in the type as well, particularly if it's a value type. These methods will be called by the default equality comparer.
- 将自定义传递给
IEqualityComparer<T>构造函数。如果您不能修改它T本身,或者如果您想要一个非默认的相等关系(例如“所有具有负用户 ID 的用户都被认为是平等的”),这是最好的选择。这几乎从未在类型本身上实现(即Foo不实现IEqualityComparer<Foo>),而是在仅用于比较的单独类型中实现。 - 通过覆盖
GetHashCodeand实现类型本身的相等性Equals(object)。理想情况下,也可以IEquatable<T>在类型中实现,特别是如果它是值类型。这些方法将由默认的相等比较器调用。
Note how none of this is in terms of an orderedcomparison - which makes sense, as there are certainly situations where you can easily specify equality but not a total ordering. This is all the same as Dictionary<TKey, TValue>, basically.
请注意,这些都不是有序比较 - 这是有道理的,因为在某些情况下,您可以轻松指定相等但不能完全排序。这Dictionary<TKey, TValue>基本上与 相同。
If you want a set which uses orderinginstead of just equality comparisons, you should use SortedSet<T>from .NET 4 - which allows you to specify an IComparer<T>instead of an IEqualityComparer<T>. This will use IComparer<T>.Compare- which will delegate to IComparable<T>.CompareToor IComparable.CompareToif you're using Comparer<T>.Default.
如果你想要一个使用排序而不是相等比较的集合,你应该使用SortedSet<T>.NET 4 - 它允许你指定一个IComparer<T>而不是一个IEqualityComparer<T>. 这将使用IComparer<T>.Compare- 这将委托给IComparable<T>.CompareTo或IComparable.CompareTo如果您正在使用Comparer<T>.Default.
回答by CodesInChaos
HashSetuses Equalsand GetHashCode().
HashSet使用Equals和GetHashCode()。
CompareTois for ordered sets.
CompareTo用于有序集。
If you want unique objects, but you don't care about their iteration order, HashSet<T>is typically the best choice.
如果您想要独特的对象,但又不关心它们的迭代顺序,HashSet<T>则通常是最佳选择。
回答by tyriker
Here's clarification on a part of the answer that's been left unsaid: The object type of your HashSet<T>doesn't have to implement IEqualityComparer<T>but instead just has to override Object.GetHashCode()and Object.Equals(Object obj).
以下是对未说明的部分答案的澄清:您的对象类型HashSet<T>不必实现IEqualityComparer<T>,而只需覆盖Object.GetHashCode()and Object.Equals(Object obj)。
Instead of this:
取而代之的是:
public class a : IEqualityComparer<a>
{
public int GetHashCode(a obj) { /* Implementation */ }
public bool Equals(a obj1, a obj2) { /* Implementation */ }
}
You do this:
你做这个:
public class a
{
public override int GetHashCode() { /* Implementation */ }
public override bool Equals(object obj) { /* Implementation */ }
}
It is subtle, but this tripped me up for the better part of a day trying to get HashSet to function the way it is intended. And like others have said, HashSet<a>will end up calling a.GetHashCode()and a.Equals(obj)as necessary when working with the set.
这很微妙,但这让我在一天的大部分时间里都试图让 HashSet 以预期的方式运行。而像其他人所说的,HashSet<a>最终会调用a.GetHashCode(),并a.Equals(obj)在必要时与设定的工作时。
回答by Nikolai Nechai
constructor HashSet receive object what implement IEqualityComparer for adding new object. if you whant use method in HashSet you nead overrride Equals, GetHashCode
构造函数 HashSet 接收对象什么实现 IEqualityComparer 添加新对象。如果你想在 HashSet 中使用方法,你需要覆盖 Equals, GetHashCode
namespace HashSet
{
public class Employe
{
public Employe() {
}
public string Name { get; set; }
public override string ToString() {
return Name;
}
public override bool Equals(object obj) {
return this.Name.Equals(((Employe)obj).Name);
}
public override int GetHashCode() {
return this.Name.GetHashCode();
}
}
class EmployeComparer : IEqualityComparer<Employe>
{
public bool Equals(Employe x, Employe y)
{
return x.Name.Trim().ToLower().Equals(y.Name.Trim().ToLower());
}
public int GetHashCode(Employe obj)
{
return obj.Name.GetHashCode();
}
}
class Program
{
static void Main(string[] args)
{
HashSet<Employe> hashSet = new HashSet<Employe>(new EmployeComparer());
hashSet.Add(new Employe() { Name = "Nik" });
hashSet.Add(new Employe() { Name = "Rob" });
hashSet.Add(new Employe() { Name = "Joe" });
Display(hashSet);
hashSet.Add(new Employe() { Name = "Rob" });
Display(hashSet);
HashSet<Employe> hashSetB = new HashSet<Employe>(new EmployeComparer());
hashSetB.Add(new Employe() { Name = "Max" });
hashSetB.Add(new Employe() { Name = "Solomon" });
hashSetB.Add(new Employe() { Name = "Werter" });
hashSetB.Add(new Employe() { Name = "Rob" });
Display(hashSetB);
var union = hashSet.Union<Employe>(hashSetB).ToList();
Display(union);
var inter = hashSet.Intersect<Employe>(hashSetB).ToList();
Display(inter);
var except = hashSet.Except<Employe>(hashSetB).ToList();
Display(except);
Console.ReadKey();
}
static void Display(HashSet<Employe> hashSet)
{
if (hashSet.Count == 0)
{
Console.Write("Collection is Empty");
return;
}
foreach (var item in hashSet)
{
Console.Write("{0}, ", item);
}
Console.Write("\n");
}
static void Display(List<Employe> list)
{
if (list.Count == 0)
{
Console.WriteLine("Collection is Empty");
return;
}
foreach (var item in list)
{
Console.Write("{0}, ", item);
}
Console.Write("\n");
}
}
}

