HashSet 如何比较元素是否相等?
我有一个 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);
}
}
当我将此类的对象列表添加到哈希集中时:
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);
一切都很好,ha.count
为 2< /code>,但是:
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));
现在 ha.count
是 3
。
- 为什么
HashSet
不尊重a
的CompareTo
方法。 HashSet
是拥有唯一对象列表的最佳方式吗?
I have a class that is 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.count
is 2
, but:
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.count
is 3
.
- Why doesn't
HashSet
respecta
'sCompareTo
method. - Is
HashSet
the best way to have a list of unique objects?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
它使用.Default。
IEqualityComparer
(EqualityComparer.Default
除非您在构造时指定不同的 EqualityComparer当您向集合中添加元素时,它将使用
IEqualityComparer.GetHashCode
查找哈希码,并存储哈希码和元素(在检查该元素是否已在集合中之后) , 当然)。要查找元素,它将首先使用
IEqualityComparer.GetHashCode
查找哈希代码,然后对于具有相同哈希代码的所有元素,它将使用IEqualityComparer查找哈希代码。 .Equals
比较实际的相等性。这意味着您有两个选择:
IEqualityComparer
传递到构造函数中。如果您无法修改T
本身,或者您想要非默认的平等关系(例如“所有具有负用户 ID 的用户都被视为平等”),那么这是最佳选择。这几乎从未在类型本身上实现(即Foo
没有实现IEqualityComparer
),而是在仅用于比较的单独类型中实现。GetHashCode
和Equals(object)
在类型本身中实现相等性。理想情况下,也在类型中实现IEquatable
,特别是如果它是值类型。这些方法将由默认的相等比较器调用。请注意,这一切都不是按有序比较的方式进行的 - 这是有道理的,因为在某些情况下,您可以轻松指定相等性,但不能指定总排序。基本上,这与
Dictionary
相同。如果您想要一个使用排序而不仅仅是相等比较的集合,则应该使用
SortedSet
- 允许您指定IComparer
而不是一个IEqualityComparer
。这将使用IComparer.Compare
- 它将委托给IComparable.CompareTo
或IComparable.CompareTo
(如果您使用的是)比较器。默认
。It uses an
IEqualityComparer<T>
(EqualityComparer<T>.Default
unless you specify a different one on construction).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).To look an element up, it will first use the
IEqualityComparer<T>.GetHashCode
to find the hash code, then for all elements with the same hash code, it will useIEqualityComparer<T>.Equals
to compare for actual equality.That means you have two options:
IEqualityComparer<T>
into the constructor. This is the best option if you can't modify theT
itself, 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.Foo
doesn't implementIEqualityComparer<Foo>
) but in a separate type which is only used for comparisons.GetHashCode
andEquals(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.Note how none of this is in terms of an ordered comparison - 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.If you want a set which uses ordering instead of just equality comparisons, you should use
SortedSet<T>
from .NET 4 - which allows you to specify anIComparer<T>
instead of anIEqualityComparer<T>
. This will useIComparer<T>.Compare
- which will delegate toIComparable<T>.CompareTo
orIComparable.CompareTo
if you're usingComparer<T>.Default
.以下是对未提及的部分答案的澄清:
HashSet
的对象类型不必实现IEqualityComparer
,而只需实现覆盖Object.GetHashCode()
和Object.Equals(Object obj)
。而不是这样:
你这样做:
这很微妙,但这让我在一天中的大部分时间里都在尝试让 HashSet 按其预期的方式运行。正如其他人所说,
HashSet
最终将在必要时调用a.GetHashCode()
和a.Equals(obj)
与该集一起工作。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 implementIEqualityComparer<T>
but instead just has to overrideObject.GetHashCode()
andObject.Equals(Object obj)
.Instead of this:
You do this:
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 callinga.GetHashCode()
anda.Equals(obj)
as necessary when working with the set.HashSet
使用Equals
和GetHashCode()
。CompareTo
用于有序集。如果您想要唯一的对象,但不关心它们的迭代顺序,
HashSet
通常是最佳选择。HashSet
usesEquals
andGetHashCode()
.CompareTo
is for ordered sets.If you want unique objects, but you don't care about their iteration order,
HashSet<T>
is typically the best choice.我来这里寻找答案,但发现所有答案的信息太多或不够,所以这是我的答案...
既然您创建了一个自定义类,您需要实现
GetHashCode
和等于
。在此示例中,我将使用类Student
而不是a
,因为它更易于遵循并且不违反任何命名约定。 这是实现的样子:我偶然发现Microsoft 的这篇文章 提供了一种非常简单的方法来实现这些(如果您使用的是 Visual Studio)。如果对其他人有帮助,请参阅以下使用 Visual Studio 在 HashSet 中使用自定义数据类型的完整步骤:
给定一个具有 2 个简单属性和一个初始值设定项的类
Student
要实现 IComparable,请添加
: IComparable
像这样:您将看到一个红色的波浪线出现,并显示一条错误消息,表明您的类没有实现 IComparable。单击建议或按 Alt+Enter 并使用建议来实施它。
您将看到生成的方法。然后,您可以编写自己的实现,如下所示:
在上面的实现中,仅比较 Id 属性,忽略 name。接下来,右键单击代码并选择“快速操作和重构”,然后选择“生成等于和 GetHashCode”
将弹出一个窗口,您可以在其中选择可以选择用于散列的属性,甚至可以实现 IEquitable(如果您愿意):
这是生成的代码:
现在,如果您尝试添加如图所示的重复项下面的内容将被跳过:
您现在可以像这样使用
.Contains
:输出:
I came here looking for answers, but found that all the answers had too much info or not enough, so here is my answer...
Since you've created a custom class you need to implement
GetHashCode
andEquals
. In this example I will use a classStudent
instead ofa
because it's easier to follow and doesn't violate any naming conventions. Here is what the implementations look like:I stumbled across this article from Microsoft that gives an incredibly easy way to implement these if you're using Visual Studio. In case it's helpful to anyone else, here are complete steps for using a custom data type in a HashSet using Visual Studio:
Given a class
Student
with 2 simple properties and an initializerTo Implement IComparable, add
: IComparable<Student>
like so:You will see a red squiggly appear with an error message saying your class doesn't implement IComparable. Click on suggestions or press Alt+Enter and use the suggestion to implement it.
You will see the method generated. You can then write your own implementation like below:
In the above implementation only the Id property is compared, name is ignored. Next right-click in your code and select Quick actions and refactorings, then Generate Equals and GetHashCode
A window will pop up where you can select which properties to use for hashing and even implement IEquitable if you'd like:
Here is the generated code:
Now if you try to add a duplicate item like shown below it will be skipped:
You can now use
.Contains
like so:Output:
HashSet
构造函数接收实现IEqualityComparer
的对象以添加新对象。如果你想使用HashSet
中的方法,你需要重写Equals()
和GetHashCode()
。HashSet<T>
constructor receive object what implementIEqualityComparer<T>
for adding new object. If you want to use method inHashSet
you need to overrrideEquals()
andGetHashCode()
.