C# 双键元组列表或字典

C# 双键元组列表或字典,c#,dictionary,C#,Dictionary,我有一组两个键对的字符串{(a1,b1),(a2,b2),(a3,b3),…}。在我的场景(a1,b1)==(b1,a1),所以(a1,b1)或(b1,a1)组合应该只作为我的集合的一部分 在C#应用程序中,我需要能够: 添加这些(a,b)元组的新对 高效(即快速)检查(a1,b1)或(b1,a1)对是否已在我的表中 您将如何使用Dictionary[Tuple[K1,K2]]或其他工具实现这样的功能?如果使用字典,有没有办法告诉它考虑(K1,K2)与(K2,K1)相同,这样我就不必添加两个

我有一组两个键对的字符串{(a1,b1),(a2,b2),(a3,b3),…}。在我的场景(a1,b1)==(b1,a1),所以(a1,b1)或(b1,a1)组合应该只作为我的集合的一部分

在C#应用程序中,我需要能够:

  • 添加这些(a,b)元组的新对
  • 高效(即快速)检查(a1,b1)或(b1,a1)对是否已在我的表中
您将如何使用Dictionary[Tuple[K1,K2]]或其他工具实现这样的功能?如果使用字典,有没有办法告诉它考虑(K1,K2)与(K2,K1)相同,这样我就不必添加两个组合?或者,将(K1,K2)和(K2,K1)两者相加是一种方法


谢谢。

这是家庭作业吗?这看起来像是一本书中的问题

  • 定义类
    ,定义相等和哈希运算符及方法。(这意味着您应该定义方法
    Equals
    、运算符
    =
    、方法
    GetHashCode
    ,如果编译器需要,还可能定义其他方法。)
  • 使用
    HashSet

  • 创建一个实现的自定义类(并确保正确重写
    GetHashCode
    )。然后你可以在一个
    散列集中使用它,两对可以自动“相等”。

    我会使用一个字典,其中键由一个函数生成,该函数接受两个字符串并生成如下散列:比较两个字符串,构建一个由“较小”字符串+分隔符+较大”字符串组成的特定字符串。这样一来,顺序就不重要了。还可以实现类似的“equals”运算符。

    创建一个存储类,公开Add(a,b)和类似函数。内部存储器可以是
    哈希集
    ,其中T是合适的字符串元组键。关于这个键和比较器,唯一重要的是使用对称的散列函数和相等函数,即(a,b)等于(b,a),因此散列函数(a,b)=散列函数(b,a)

    如前所述,许多散列函数都具有此属性,例如散列值的总和和异或。我选择不使用xor,因为这意味着所有对等字符串对的哈希值都为零,如果可能存在对等字符串对,这可能会导致查找效率低下

    下面的实现假定所有字符串均为非null,但没有错误检查

    public class Storage
    {
       private HashSet<Key> set;
    
       public Storage()
       {
          set = new HashSet<Key>(new Key.Comparer());
       }
    
       public void Add(string a, string b)
       {
          set.Add(new Key{A=a, B=b});
       }
    
       public bool Contains(string a, string b)
       {
          return set.Contains(new Key{A=a, B=b});
       }
    
       internal class Key
       {
           internal String A { get; set; }
           internal String B { get; set; }
           internal class Comparer : IEqualityComparer<Key>
           {
              public bool Equals(Key x, Key y)
              {
                 return (x.A == y.A && x.B == y.B) || (x.A == y.B && x.B == y.A);
              }
              public int GetHashCode(Key k)
              {
                 int aHash = k.A.GetHashCode();
                 int bHash = k.B.GetHashCode();
                 // Hash for (x,y) same as hash for (y,x)
                 if (aHash > bHash)
                    return bHash * 37 + aHash;
                 return aHash * 37 + bHash;
              }
           }
       }
    
    }
    
    公共类存储
    {
    私有哈希集;
    公共存储()
    {
    set=newhashset(newkey.Comparer());
    }
    公共无效添加(字符串a、字符串b)
    {
    Add(新键{A=A,B=B});
    }
    公共布尔包含(字符串a、字符串b)
    {
    包含(新键{A=A,B=B});
    }
    内部类密钥
    {
    内部字符串A{get;set;}
    内部字符串B{get;set;}
    内部类比较器:IEqualityComparer
    {
    公共布尔等于(键x,键y)
    {
    返回(x.A==y.A和x.B==y.B)| |(x.A==y.B和x.B==y.A);
    }
    public int GetHashCode(键k)
    {
    int-aHash=k.A.GetHashCode();
    int bHash=k.B.GetHashCode();
    //(x,y)的哈希值与(y,x)的哈希值相同
    如果(aHash>bHash)
    返回bHash*37+aHash;
    返回aHash*37+bHash;
    }
    }
    }
    }
    
    显然,我不能键入lt或gt字符,它们不会被转义,所以我用括号/括号替换了它们。您需要如何处理集合?例如,您希望如何查找值?@JonSkeet我只需要快速检查(a1,b1)和(b1,a1)是否已在集合中。我正在处理大量的这些事情,所以它需要很快。谢谢。不,不是作业问题。。。我只是想用一种不含糊的方式来解释。但是如果我重写Equals方法,这不会减慢在列表中找到项目的速度吗?这不是违背了快速访问的目的吗?谢谢。我们这里几乎不能谈论减速,因为您只需要一种方法来比较两个键。如果您不提供一种比较键的方法,它将不起作用。这将正确地“索引”项目吗?我试图阻止“扫描”,即对每个项目检查调用“等于”。@pbz:只要您有一个合理的哈希代码,就可以了。GetHashCode应该是什么样子?key1+key2或key2+key1?@pbz其实并不重要,只要
    产生完全相同的哈希代码。一个简单的加法就行了,或者一个XOR…这可能是基本的,但我显然遗漏了一些东西。你能给我一个例子吗,一对(“abc”,“def”)的哈希值与一对(“def”,“abc”)的哈希值相同?谢谢。是的,使用String.Compare(str1,str2)是一个老习惯,它总是组合散列,比如“a*素数+b”。当组合散列代码以避免冲突时,这是通常的简单模式,但我现在想起来,在做这样的对称散列时,我实际上不确定这是否严格必要。这可能是因为它在a,b之间产生了不对称性,如果我只对散列求和的话,这是不存在的。也许有人可以在这里填写,已经很晚了……当aHash和bHash是散列时,为什么不简单地将它们异或在一起呢?这不好吗?如果一个人经常添加相等字符串的元组,那就不好了,因为所有这些元组都得到相同的散列(零)。