使用等于。 GetHashCode 不是
Equals is used. GetHashCode is not
我已经实现了下面的class:
public class carComparer : IEqualityComparer<Car>
{
public bool Equals(Car car1, Car car2)
{
if (car1 == null || car2 == null)
return false;
return (car1.description == car2.description);
}
public int GetHashCode(Car car)
{
unchecked
{
int hash = 17;
hash = hash * 29 + car.id.GetHashCode();
hash = hash * 29 + car.description.GetHashCode();
return hash;
}
}
}
现在看这个:
Car p1 = new Car() { id = Guid.NewGuid(), description = "Test1" };
Car p2 = new Car() { id = Guid.NewGuid(), description = "Test1" };
Car p3 = new Car() { id = Guid.NewGuid(), description = "Test1" };
Car p4 = new Car() { id = Guid.NewGuid(), description = "Test1" };
var hash = new HashSet<Car>();
hash.Add(p1);
hash.Add(p2);
var hash2 = new HashSet<Car>();
hash2.Add(p3);
hash2.Add(p4);
var carComparer = new CarComparer();
Assert.That(hash, Is.EquivalentTo(hash2).Using(carComparer));
我在 .equals 和 .hashcode 中设置了断点。使用等于;但 GetHashCode 不是。为什么?
GetHashCode 通常用于哈希-table-查找。
GetHashCode
不必保证唯一,因此不是有效的 IsEqual
测试。
要使用 GetHashCode,请使用 HashSet 的此构造函数:
https://msdn.microsoft.com/en-us/library/bb359100(v=vs.110).aspx
因此,为了使用 GetHashCode
方法,您需要使用:
var hash = new HashSet<Car>(carComparer);
请注意,将您的对象添加到哈希集时将验证哈希。
然后在 HashSet.Add
方法中使用比较器,在此调用中:
private int InternalGetHashCode(T item)
{
if ((object) item == null)
return 0;
//this._comparer is set during the constructor call
return this._comparer.GetHashCode(item) & int.MaxValue;
}
由于显而易见的原因,这使得 Comparer
成为只读的 属性。
所以,总结一下;
未使用 GetHashCode
,因为它通常用于哈希-table-查找创建,您需要在开始添加项目之前将其提供给 HashSet .
由于显而易见的原因,使用了IsEqual
;如果不是:请参阅@dasblinkenlight 的回答。
发生这种情况是因为 IsEquivalent
中使用的算法来决定等效性:实现从您期望的 collection 构造他们所谓的 "collection tally" object,然后尝试从中删除实际 collection 的项目 one-by-one:
public bool TryRemove(IEnumerable c) {
foreach (object o in c)
if (!TryRemove(o))
return false;
return true;
}
public bool TryRemove(object o) {
for (int index = 0; index < list.Count; index++)
if (ItemsEqual(list[index], o)) {
list.RemoveAt(index);
return true;
}
return false;
}
可以看到NUnit使用了相对低效的O(n2)算法,而不是构造一个O(n)效率的哈希集。这对于较大的集合很重要,但由于单元测试中的典型 collection 只有几个项目,因此不会有明显的差异。
ItemsEqual
使用相等比较器中的 Equals
,但它不需要散列码功能 (source code)。
您正在使用 NUnit Is.EquivalentTo
比较两个 HashSet
。它没有理由调用 GetHashCode
- 它基本上比较两个集合的成员是否相等。这就是为什么从不调用 GetHashCode
而调用 Equals
来比较来自不同 HashSet
的两个项目是否相等。您的哈希集也可以是列表或任何其他可枚举的 - 在比较两个集合时不会改变任何内容。
您可能希望在将项目添加到 HashSet
时调用 GetHashCode
- 但事实并非如此,因为此时您的 carComparer
尚不清楚 - 您不知道不要将它传递给 HashSet
构造函数。如果你会这样做:
var hash = new HashSet<Car>(new carComparer());
然后GetHashCode
将在您向相应的HashSet
添加新项目时被调用。
我已经实现了下面的class:
public class carComparer : IEqualityComparer<Car>
{
public bool Equals(Car car1, Car car2)
{
if (car1 == null || car2 == null)
return false;
return (car1.description == car2.description);
}
public int GetHashCode(Car car)
{
unchecked
{
int hash = 17;
hash = hash * 29 + car.id.GetHashCode();
hash = hash * 29 + car.description.GetHashCode();
return hash;
}
}
}
现在看这个:
Car p1 = new Car() { id = Guid.NewGuid(), description = "Test1" };
Car p2 = new Car() { id = Guid.NewGuid(), description = "Test1" };
Car p3 = new Car() { id = Guid.NewGuid(), description = "Test1" };
Car p4 = new Car() { id = Guid.NewGuid(), description = "Test1" };
var hash = new HashSet<Car>();
hash.Add(p1);
hash.Add(p2);
var hash2 = new HashSet<Car>();
hash2.Add(p3);
hash2.Add(p4);
var carComparer = new CarComparer();
Assert.That(hash, Is.EquivalentTo(hash2).Using(carComparer));
我在 .equals 和 .hashcode 中设置了断点。使用等于;但 GetHashCode 不是。为什么?
GetHashCode 通常用于哈希-table-查找。
GetHashCode
不必保证唯一,因此不是有效的 IsEqual
测试。
要使用 GetHashCode,请使用 HashSet 的此构造函数:
https://msdn.microsoft.com/en-us/library/bb359100(v=vs.110).aspx
因此,为了使用 GetHashCode
方法,您需要使用:
var hash = new HashSet<Car>(carComparer);
请注意,将您的对象添加到哈希集时将验证哈希。
然后在 HashSet.Add
方法中使用比较器,在此调用中:
private int InternalGetHashCode(T item)
{
if ((object) item == null)
return 0;
//this._comparer is set during the constructor call
return this._comparer.GetHashCode(item) & int.MaxValue;
}
由于显而易见的原因,这使得 Comparer
成为只读的 属性。
所以,总结一下;
未使用 GetHashCode
,因为它通常用于哈希-table-查找创建,您需要在开始添加项目之前将其提供给 HashSet .
由于显而易见的原因,使用了IsEqual
;如果不是:请参阅@dasblinkenlight 的回答。
发生这种情况是因为 IsEquivalent
中使用的算法来决定等效性:实现从您期望的 collection 构造他们所谓的 "collection tally" object,然后尝试从中删除实际 collection 的项目 one-by-one:
public bool TryRemove(IEnumerable c) {
foreach (object o in c)
if (!TryRemove(o))
return false;
return true;
}
public bool TryRemove(object o) {
for (int index = 0; index < list.Count; index++)
if (ItemsEqual(list[index], o)) {
list.RemoveAt(index);
return true;
}
return false;
}
可以看到NUnit使用了相对低效的O(n2)算法,而不是构造一个O(n)效率的哈希集。这对于较大的集合很重要,但由于单元测试中的典型 collection 只有几个项目,因此不会有明显的差异。
ItemsEqual
使用相等比较器中的 Equals
,但它不需要散列码功能 (source code)。
您正在使用 NUnit Is.EquivalentTo
比较两个 HashSet
。它没有理由调用 GetHashCode
- 它基本上比较两个集合的成员是否相等。这就是为什么从不调用 GetHashCode
而调用 Equals
来比较来自不同 HashSet
的两个项目是否相等。您的哈希集也可以是列表或任何其他可枚举的 - 在比较两个集合时不会改变任何内容。
您可能希望在将项目添加到 HashSet
时调用 GetHashCode
- 但事实并非如此,因为此时您的 carComparer
尚不清楚 - 您不知道不要将它传递给 HashSet
构造函数。如果你会这样做:
var hash = new HashSet<Car>(new carComparer());
然后GetHashCode
将在您向相应的HashSet
添加新项目时被调用。