假设我有一个班级
public class MyClass
{
public string Type { get; set; }
public int Id { get; set; }
}
我有一个集合类,它只是一个强类型列表
public class MyClassList : List<MyClass>
{
public MyClassList(IEnumerable<MyClass> enumerable) : base (enumerable) {}
}
我希望MyClassList
能够根据内容为MyClassList
生成唯一的哈希码。 MyClass
的哈希码应该基于这两个属性。 即使对象的顺序不同,MyClassList
的哈希码也应该相同。
为了处理排序问题,我想我可以在生成哈希码之前订购列表,但我不确定如何生成列表的哈希码。
答案 0 :(得分:4)
为了获得最佳性能,我会尽量避免在每次调用GetHashCode
时迭代整个集合。 GetHashCode
的目的是将性能提高到比评估每个元素更好的程度。所以当列表中的元素像这样改变时,我可能会尝试维护哈希码。
class Program
{
static void Main(string[] args)
{
MyClassList l = new MyClassList() { new MyClass() {Type="Bob", Id=1}, new MyClass() {Type="Jones", Id=2}};
MyClassList l2 = new MyClassList() { new MyClass() { Type = "Jones", Id = 2 }, new MyClass() { Type = "Bob", Id = 1 } };
MyClassList l3 = new MyClassList() { new MyClass() { Type = "Jones", Id = 2 }};
Console.WriteLine("{0} {1} {2}", l.GetHashCode(), l2.GetHashCode(), l3.GetHashCode());
l3.Add(new MyClass() { Type = "Bob", Id = 1 });
Console.WriteLine("{0}", l3.GetHashCode());
}
}
public class MyClass
{
public string Type { get; set; }
public int Id { get; set; }
public override int GetHashCode()
{
return (Type.GetHashCode() % 0x8000) | (int)((uint)Id.GetHashCode() & 0xFFFF0000);
}
}
public class MyClassList : IList<MyClass>
{
List<MyClass> internalList;
int hashCode = 0;
public MyClassList()
{
internalList = new List<MyClass>();
}
private void IncludeInHash(MyClass item)
{
hashCode ^= item.GetHashCode();
}
private void ExcludeFromHash(MyClass item)
{
IncludeInHash(item);
}
public override int GetHashCode()
{
return hashCode;
}
public int IndexOf(MyClass item)
{
return internalList.IndexOf(item);
}
public void Insert(int index, MyClass item)
{
internalList.Insert(index, item);
// Make sure Insert is successful (doesn't throw an exception) before affecting the hash
IncludeInHash(item);
}
public void RemoveAt(int index)
{
MyClass reduce = internalList[index];
internalList.RemoveAt(index);
// Make sure RemoveAt is successful before affecting the hash
ExcludeFromHash(reduce);
}
public MyClass this[int index]
{
get
{
return internalList[index];
}
set
{
MyClass reduce = internalList[index];
internalList[index] = value;
// Make sure these happen atomically; don't allow exceptions to prevent these from being accurate.
ExcludeFromHash(reduce);
IncludeInHash(value);
}
}
public void Add(MyClass item)
{
internalList.Add(item);
IncludeInHash(item);
}
public void Clear()
{
internalList.Clear();
hashCode = 0;
}
public bool Contains(MyClass item)
{
return internalList.Contains(item);
}
public void CopyTo(MyClass[] array, int arrayIndex)
{
internalList.CopyTo(array, arrayIndex);
}
public int Count
{
get { return internalList.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(MyClass item)
{
if (internalList.Remove(item))
{
ExcludeFromHash(item);
return true;
}
else
return false;
}
public IEnumerator<MyClass> GetEnumerator()
{
return internalList.AsReadOnly().GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
答案 1 :(得分:3)
只需添加每个元素的所有哈希码。
public class MyClass
{
...
public override int GetHashCode()
{
return Type.GetHashCode() + Id;
}
}
public class MyClassList : List<MyClass>
{
public override int GetHashCode()
{
int code = 0;
for( int i = 0; i < Count; ++i )
code += this[i].GetHashCode();
return code;
}
}
答案 2 :(得分:1)
我提出这个解决方案(我没有实现Equals方法):
public class MyClass
{
public string Type { get; set; }
public int Id { get; set; }
public override int GetHashCode()
{
int hash = 17;
hash = hash + 23 * this.Type.GetHashCode();
hash = hash + 23 * this.Id.GetHashCode();
return hash;
}
}
public class MyClassList : List<MyClass>
{
public MyClassList(IEnumerable<MyClass> enumerable) : base(enumerable) { }
public override int GetHashCode()
{
return this.Aggregate(17, (state, current) => state * 23 + current.GetHashCode());
}
}
生成哈希码的方法受到Microsoft方法的启发,以计算匿名对象的哈希值。
答案 3 :(得分:1)
clto给出的解决方案有效。这是一个替代方案:按照一些总排序对列表进行排序(任何排序都可以,只要它是明确的)。然后,您可以使用任何常规方法计算哈希码。您不需要订单独立性。你甚至可以使用加密哈希函数。
答案 4 :(得分:0)
如果订单不重要,那么您应该使用本身就是集合的集合,而不是列表。
此外,通常最好不要继承收藏品;改为使用构图。
因此,对于集合,您可以使用HashSet
,因为它将设置语义。
让MyClass
使用这两个属性,因为它的身份只是覆盖它的等于并获得哈希代码实现,或者如果你不能或不想那样创建IComparer<MyClass>
。
public class MyClass:IEquatable<MyClass>
{
public string Type { get; set; }
public int Id { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as MyClass);
}
public bool Equals(MyClass other)
{
if (other == null)
return false;
return Type == other.Type &&
Id == other.Id;
}
public override int GetHashCode()
{
return Type.GetHashCode() * 79 + Id;
}
}
然后你的收藏就像:
HashSet<MyClass> set = new HashSet<MyClass>();
如果你想比较各种套装,只需使用:
HashSet<MyClass>.CreateSetComparer();