为确保复合键的两侧也是唯一的,元组不会将其剪切。而是制作自己的密钥,在相等检查器中检查这一点。
public struct CompositeKey<T1, T2> : IEquatable<CompositeKey<T1, T2>>
{
private static readonly EqualityComparer<T1> t1Comparer = EqualityComparer<T1>.Default;
private static readonly EqualityComparer<T2> t2Comparer = EqualityComparer<T2>.Default;
public T1 Key1;
public T2 Key2;
public CompositeKey(T1 key1, T2 key2)
{
Key1 = key1;
Key2 = key2;
}
public override bool Equals(object obj) => obj is CompositeKey<T1, T2> && Equals((CompositeKey<T1, T2>)obj);
public bool Equals(CompositeKey<T1, T2> other)
{
return t1Comparer.Equals(Key1, other.Key1)
&& t2Comparer.Equals(Key2, other.Key2);
}
public override int GetHashCode() => Key1.GetHashCode();
}
所以字典适用于桶。它根据生成的哈希码将所有密钥放入桶中GetHashCode()。然后它使用 for 循环搜索该存储桶Equals()。这个想法是桶应该尽可能小(最好是一个项目)。
所以我们可以通过控制哈希码来控制一个键何时匹配,以及有多少桶/项目。如果我们返回一个像 0 这样的常量哈希码,那么所有内容都在同一个存储桶中,这取决于比较每个项目的相等方法。
此比较器仅返回第一个关键项的哈希。假设第一个关键项应该是唯一的,这就足够了。每个存储桶仍然应该是一个项目,并且在进行查找(使用完全等于方法)时,还会检查第二个键以确保类型是相同的值。
如果要ValueTuple用作键类型,可以将自定义比较器传递给字典以达到相同的效果。
public class CompositeValueTupleComparer<T1, T2> : IEqualityComparer<(T1, T2)>
{
private static readonly EqualityComparer<T1> t1Comparer = EqualityComparer<T1>.Default;
private static readonly EqualityComparer<T2> t2Comparer = EqualityComparer<T2>.Default;
public bool Equals((T1, T2) x, (T1, T2) y) =>
t1Comparer.Equals(x.Item1, y.Item1) && t2Comparer.Equals(x.Item2, y.Item2);
public int GetHashCode((T1, T2) obj) => obj.Item1.GetHashCode();
}
new Dictionary<(int, string), Book>(new CompositeValueTupleComparer<int, string>());