【发布时间】:2019-08-30 14:49:35
【问题描述】:
我有一个模型类 Class1,我想比较 Class1 的两个实例是否相同(结构相等)。
public class Class1 : IEquatable<Class1>
{
public string Id { get; set; }
public string Name { get; set; }
public IList<Class2> Class2s { get; set; }
public bool Equals(Class1 other)
{
return QuestName.Equals(other.QuestName)
&& Class2s.OrderBy(c => c.Id).SequenceEqual(other.Class2s.OrderBy(c => c.Id));
//Below method is very fast but not so accurate
//because 2 objects with the same hash code may or may not be equal
//return GetHashCode() == other.GetHashCode();
}
public override bool Equals(object obj)
{
return obj is Class1
&& this.Equals(obj as Class1);
}
public override int GetHashCode()
{
unchecked
{
int hash = 13;
hash = (hash * 7) + Name.GetHashCode();
foreach (var c2 in Class2s.OrderBy(c => c.Id))
{
hash = (hash * 7) + c2.GetHashCode();
}
return hash;
}
}
}
public class Class2 : IEquatable<Class2>
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Class3> Class3s { get; set; }
public bool Equals(Class2 other)
{
return Id == other.Id
&& Name.Equals(other.Name)
&& Class3s.OrderBy(c => c.Id).SequenceEqual(other.Class3s.OrderBy(c => c.Id));
}
public override bool Equals(object obj)
{
return obj is Class2
&& this.Equals(obj as Class2 );
}
public override int GetHashCode()
{
unchecked
{
int hash = 13;
hash = (hash * 7) + Id.GetHashCode();
hash = (hash * 7) + Name.GetHashCode();
foreach (var c3 in Class3s.OrderBy(c => c.Id))
{
hash = (hash * 7) + c3.GetHashCode();
}
return hash;
}
}
}
public class Class3 : IEquatable<Class3>
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Class4> Class4s { get; set; }
public bool Equals(Class3 other)
{
return Id == other.Id
&& Name.Equals(other.Name)
&& Class4s.OrderBy(c => c.Id).SequenceEqual(other.Class4s.OrderBy(c => c.Id));
}
public override bool Equals(object obj)
{
return obj is Class3
&& this.Equals(obj as Class3);
}
public override int GetHashCode()
{
unchecked
{
int hash = 13;
hash = (hash * 7) + Id.GetHashCode();
hash = (hash * 7) + Name.GetHashCode();
foreach (var c in Class4s.OrderBy(c => c.Id))
{
hash = (hash * 7) + c.GetHashCode();
}
return hash;
}
}
}
public class Class4 : IEquatable<Class4>
{
public int Id { get; set; }
public string Name { get; set; }
public bool Equals(Class4 other)
{
return Id.Equals(other.Id)
&& Name.Equals(other.Name);
}
public override bool Equals(object obj)
{
return obj is Class4
&& this.Equals(obj as Class4);
}
public override int GetHashCode()
{
unchecked
{
int hash = 13;
hash = (hash * 7) + Id.GetHashCode();
hash = (hash * 7) + Name.GetHashCode();
return hash;
}
}
}
我说两个 Class1 对象在以下情况下相等:
1. 他们有相同的Name
2.它们有相同的Class2对象(它们的顺序无关紧要)
两个Class2 对象相等:
1.他们有相同的Id
2.他们有相同的名字
3.它们有相同的Class3对象(它们的顺序无关紧要)
两个Class3 对象相等:
1.他们有相同的Id
2.他们有相同的名字
3.它们有相同的Class4对象(它们的顺序无关紧要)
两个Class4 对象相等:
1.他们有相同的Id
2.他们有相同的名字
我使用Equals 方法比较它们并像这样测量运行时间:
Class1 obj1 = GetFirstClass1Object();
Class1 obj2 = GetSecondClass1Object();
var startTime = DateTime.Now;
bool equals = obj1.Equals(obj2);
var elaspedTime = DateTime.Now.Substract(startTime)
上述解决方案效果很好,但速度很慢。
我知道如果我们将obj1 和obj2 展平,它们每个都包含3500 个Class4 对象,比较obj1 和obj2 大约需要12 秒。
有没有更快的方法来做到这一点?我可以以某种方式利用散列来加快速度吗?
此外,obj1 和 obj2 中的 Class2、Class3 和 Class4 对象的数量将始终相同
【问题讨论】:
-
尝试以下操作:(Id + "^" + Name + string.Join("^",Class4s.Select(x => x.Id))).ToHash();如果 Id 是唯一的,则不需要 Name 和 Id。
-
这不是您问题的答案,它可能只是您的代码示例中的一个错字,但以防万一您应该知道您对
Equals(object)的覆盖都是无限递归的。 -
@jdweng 你能详细说明一下吗?是的,我确实需要
Id和Name都等于说对象是平等的。 -
Ids 通常是唯一的。你需要两者吗?如果 ID(如果需要,名称是唯一的)Joshua 是错误的。你不需要递归。旁边的哈希不需要是唯一的。哈希是 EQUAL 的第一步,最小化重复哈希会加速比较。在执行散列后,将执行相等方法作为第二步,以消除散列可能重复的情况。在每种情况下,您都可以只让哈希返回 1。如果您有一个来自“a”、“ab”、“bcd”、“cd”等词的哈希。并结合任意两者。你可以得到“abcd”。添加“^”你会得到“a^bcd”和“ab^cd”。
-
我认为这里没有太大的优化空间。瓶颈显然是频繁的
OrderBy调用,使用IList时您无法真正避免。我会去审查模型,并检查是否可以移动到预排序或键索引的数据结构,如IDictionary,或缓存以前相等性测试的结果。
标签: c# list performance linq comparison