【问题标题】:Is there a data structure that holds sets of data in .NET?.NET 中是否有保存数据集的数据结构?
【发布时间】:2010-02-09 23:20:43
【问题描述】:

我正在寻找一种类似于字典的数据结构,它将所有相关项的集合返回给一个键。

例如,我会这样使用它:

var data = new FancyDataStructure();

data.Add(new string[] {"Elizabeth", "Liz", "Betty"});
data.Add(new string[] {"Bob", "Robert", "Rob"});

string[] alternateNames1 = data["Betty"];
string[] alternateNames2 = data["Liz"]

在本例中,alternateNames1 将是一个包含“Liz”和“Elizabeth”的数组,alternateNames2 将是一个包含“Elizabeth”和“Betty”的数组。

我不想重新发明这个,但我找不到这种结构的任何例子。

更新

感谢那些回信提出建议的人。许多人建议使用某些版本的Dictionary<string, IEnumerable<string>>。目前我正在使用这种方法,但它实际上并没有满足要求,而且维护起来非常困难。每个列表中的每个值都需要能够充当集合中添加到它的每个其他值的键。

因此,鉴于以下情况:

data.Add(new string[] {"Elizabeth", "Liz"}
data.Add(new string[] {"Liz", "Betty"}
alternates = data["Betty"];

我希望替代品现在包含“Elizabeth”和“Liz”。

看起来我可能只需要构建这样一个结构来满足我的需要。让想法不断涌现!

布赖恩

【问题讨论】:

标签: .net data-structures set


【解决方案1】:

您的问题听起来确实是graphing 问题。将名称视为节点,将集合中的成员资格视为边。从这个角度来看,您需要一个能够很好地处理稀疏图的数据结构,例如 adjacency list。当然,这与您已经在使用 Dictionary<string, IEnumerable<string>> 所做的类似,但以这种方式思考它可能会引导您找到一些有用的实现和算法。

【讨论】:

  • 我认为您将其视为无法从现有结构轻松组合的东西可能是正确的。我已经想出了一种解决方法来满足我的需求,但是如果我有更多的时间,我实际上会尝试构建这个新结构,如果我想出一些有用的东西,我会在这里发布。
【解决方案2】:

System.Collections.Generic 命名空间和 System.Collections 加载了 KeyValue 对字典、排序字典、列表对象等等。

System.Collections.Generic.Dictionary<int, string> dic = new Dictionary<int, string>();
        dic.Add(1, test);

或字典中的嵌套列表

Dictionary<string, List<string>> dic = new Dictionary<string, List<string>>();
List<string> alternatives = new List<string>();
alternatives.Add("Brenda");
dic.Add("Betty", alternatives);

【讨论】:

    【解决方案3】:

    只是另一个方向的想法 - 强类型数据集似乎有很多用途。并且序列化为字节数组,它们对于移动多维结构化数据非常快。

    迭代和 Linq 功能是内置的。

    对于很多东西来说可能有点矫枉过正,但我​​有很多地方将整个数据集存储在 SQL 中的一个 varbinary(max) 列中。

    【讨论】:

      【解决方案4】:

      这样的事情看起来很简单。

      var data = new List<string[]>();
      
      data.Add(new string[] {"Elizabeth", "Liz", "Betty"});
      data.Add(new string[] {"Bob", "Robert", "Rob"});
      
      var alternateNames1 = data.Where(x =>x.Contains("Betty")).Select(x => x.Where(y => y != "Betty"));
      

      【讨论】:

      • 这不适用于大型“集合”,您仍然会有 O(n) 查找;如果没问题,那就去吧。
      【解决方案5】:

      我只会使用Dictionary&lt;string, IEnumerable&lt;string&gt;&gt; 类型。要从列表列表中构建此结构,您可以使用如下代码:

      var alternateNames = new string[][] {
          new string[] { "Elizabeth", "Liz", "Betty" },
          new string[] { "Bob", "Robert", "Rob" }, };
      var altNameLookup = 
          (
              from nameList in alternateNames
              from name in nameList
              select new { 
                  Name = name, NameList = nameList.Except(new string[] { name } ) }
          ).ToDictionary(o => o.Name, o => o.NameList);
      

      【讨论】:

        【解决方案6】:

        您基本上有一个字典,其中多个键映射到相同的值。没有支持您想要的操作的内置数据结构,但它很容易在 .NET 中表示为 Dictionary{string, HashSet{string}}

        static void AddNames(Dictionary<string, HashSet<string>> map, params string[] names)
        {
            for (int i = 0; i < names.Length; i++)
            {
                HashSet<string> value;
                if (!map.TryGetValue(names[i], out value))
                {
                    value = new HashSet<string>();
                    map.Add(names[i], value);
                }
        
                for (int j = 0; j < names.Length; j++)
                {
                    value.Add(names[j]);
                }
            }
        }
        
        static void Main(string[] args)
        {
            Dictionary<string, HashSet<string>> names = new Dictionary<string,HashSet<string>>();
            AddNames(names, "Chris", "Christopher");
            AddNames(names, "Christina", "Chrissy", "Chris");
        
            HashSet<string> relatedToChris = names["Chris"];                // gets "Chris", "Christina", "Chrissy", "Christopher";
            HashSet<string> namesRelatedToChristinia = names["Christina"];  // gets "Christina", "Chrissy", "Chris";
        }
        

        您可以将您的数据结构视为一个有向图,其中每个节点都有一条连接到其相关名称的边。由于有 n^2 条边,字典需要 O(n^2) 时间来插入和记忆。不可能将查找时间缩短到更好的水平。

        幸运的是,由于它是作为字典实现的,因此查找仍然是 O(1)。删除是 O(m),其中 m 是与键相关的值的数量。

        【讨论】:

        • 似乎每个键都有一个不同的哈希集,即使对于几个相关的键,它们的哈希集的内容是相同的。因此,您具有如此高的插入和删除复杂性。为所有相关键设置一个共享哈希集不是更好吗?然后插入将是 O(m)(假设 O(1) 键查找),删除将是 O(1)。
        【解决方案7】:

        事实上的 alt.net 标准在 Iesi.Collections 中,但基类库在 dotnet 3.5 或更高版本中只有 HashSet&lt;T&gt;

        我在 linq 中使用了“group by”like 子句来轻松地从任意 IEnumerable&lt;T&gt; 集合中删除重复项,但这并不能完全为您提供设置语义。

        HashSet 接近你想要的。

        根据您的要求,我认为没有现成的东西可以将字符串映射到预先存在的集合;基本上,您必须编写一个类,该类采用 StoreAssociations&lt;&lt;T&gt;&gt;(IEnumerable&lt;&lt;T&gt;&gt; names) 之类的方法,将 IEnumerable 转换为 HashSet,并遍历 HashSet 中的每个项目以将 IDictionary&lt;string,HashSet&lt;T&gt;&gt; 中的映射添加到新创建的哈希集。

        【讨论】:

          【解决方案8】:

          一对数据结构怎么样:Dictionary&lt;string, Guid&gt;Dictionary&lt;Guid, List&lt;string&gt;&gt;

          要添加一对键 (a, b) [您可以将较大的添加分解成对 (1+2, 2+3, ...]),请执行以下操作:-

          在第一本词典中查找 a 和 b。
          如果都不存在,则创建一个新的 Guid 并将 (a,g) 和 (b,g) 添加到第一个字典,并将 (g,List{a}) 和 (g,List{b}) 添加到第二个字典。

          如果存在一个,比如说 a,从它 (g) 中获取 guid,并将另一个 (b, g) 添加到第一个字典中,然后将 b 添加到第二个字典中 [g] 处的列表末尾。

          如果两者都存在并且它们具有相同的 guid - 无事可做。

          如果两者都存在并且它们具有不同的 guid,您需要合并这两个集合 // 这是大多数其他建议的解决方案似乎都缺少的东西 // 所以选择一个 Guid 来消除,从第二个字典中获取它,将字符串列表添加到另一个条目,然后删除该条目。最后标记第一本词典中该列表中的所有单词。

          要获取所有相关单词,请在第一个字典中查找 Guid,然后从第二个字典中获取列表。

          当然,静态递增的 long 值可能比 Guid 更好。

          【讨论】:

          • 不过,您可以称其为“关系解决方案”:) 值得在您的解决方案中扩展查找/插入/删除的算法复杂性。
          【解决方案9】:

          或者,由于 List 是一种引用类型,您可以执行以下操作...

          Dictionary<string, List<string>> dict = new ...
          

          进行如下:-

          添加单个关联 (a = b) {从等价列表分解}

          在字典中查找 a 和 b

          如果两者都不存在

          dict.Add(a, new List<string>(){a}); dict.Add(b, new List<string>(){b});
          

          如果存在,比如说,一个

          var list = dict[a];
          list.Add(b);
          dict.Add(b, list);
          

          如果两者都存在并且列表相同(对象比较),您就完成了。

          如果两者都存在且列表不同:

          var list1 = dict[a];
          var list2 = dict[b];
          list1.AddRange(list2);
          dict.Remove(b);
          dict.Add(b, list1);
          

          【讨论】:

            【解决方案10】:

            我写了一些代码,我不知道它的效率如何,但我认为它可以满足您的需求。

            这是你的结构

            class FancyDataStructure
            {
                private IDictionary<string, HashSet<string>> dictionary 
                    = new Dictionary<string, HashSet<string>>();
            
                public void Add(params string[] names)
                {
                    HashSet<string> set = new HashSet<string>(names);
                    for (int i = 0; i < names.Length; i++)
                    {
                        if (!dictionary.ContainsKey(names[i]))
                        {
                            dictionary.Add(names[i], set);
                        }
                        else
                        {
                            HashSet<string> union = 
                            new HashSet<string>(set.Union<string>(dictionary[names[i]]));
                            set = union;
                            foreach (string oldName in dictionary[names[i]])
                            {
                                dictionary[oldName] = union;
                            }
                            for (int j = 0; j < i; j++)
                            {
                                if (!dictionary.ContainsKey(names[j]))
                                {
                                    dictionary.Add(names[j], union);
                                }
                            }
                        }
                    }
                }
            
                public string[] this[string key]
                {
                    get
                    {
                        List<string> result = dictionary[key].ToList<string>();
                        result.Remove(key);
                        return result.ToArray();
                    }
                }
            }
            

            你可以像这样使用它

                static void Main(string[] args)
                {
            
                    FancyDataStructure data = new FancyDataStructure();
            
                    data.Add("Elizabeth", "Liz");
                    data.Add("Liz", "Betty");
            
                    string[] alternates = data["Betty"];
                    foreach (var item in alternates)
                    {
                        Console.WriteLine(item);
                    }
                }
            

            【讨论】:

              【解决方案11】:

              【讨论】:

              • C5 Set 对 BCL HashSet 有什么影响?
              • -1:OP 的标题可能有点误导,但他的问题不是。 OP 不想要一个集合类,他想要一种特殊的字典,它将多个键映射到同一个值。
              • HashSet 在较旧的框架上不存在 :-)(在相对较新的 3.5 之前)
              【解决方案12】:

              尝试使用字典,例如:

              Dictionary<string, List<string>>
              

              所以是一个字符串键的字典,其值为 List

              【讨论】:

                猜你喜欢
                • 2010-09-05
                • 1970-01-01
                • 2015-10-23
                • 2012-11-04
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-02-01
                • 1970-01-01
                相关资源
                最近更新 更多