【问题标题】:c# dictionary How to add multiple values for single key?c#字典如何为单个键添加多个值?
【发布时间】:2012-04-22 19:24:03
【问题描述】:

我已经创建了字典对象

Dictionary<string, List<string>> dictionary =
    new Dictionary<string,List<string>>();

我想将字符串值添加到给定单个键的字符串列表中。 如果密钥不存在,那么我必须添加一个新密钥。 List&lt;string&gt; 没有预定义,我的意思是我没有创建任何列表对象然后提供给dictionary.Add("key",Listname)。如何在dictionary.Add("key",Listname) 中动态创建此列表对象,然后将字符串添加到此列表中。如果我必须添加 100 个键,那么我是否必须在执行 dictionary.Add 指令之前创建 100 个列表,并且我是否必须定义此列表的内容?

谢谢。

【问题讨论】:

  • 很遗憾他们没有包含可变的Lookup 实现。很多逻辑已经存在,只是不能添加项目。

标签: c# list dictionary


【解决方案1】:

更新:使用TryGetValue检查是否存在,在您拥有列表的情况下只进行一次查找:

List<int> list;

if (!dictionary.TryGetValue("foo", out list))
{
    list = new List<int>();
    dictionary.Add("foo", list);
}

list.Add(2);


原文: 检查是否存在并添加一次,然后键入字典以获取列表并正常添加到列表中:
var dictionary = new Dictionary<string, List<int>>();

if (!dictionary.ContainsKey("foo"))
    dictionary.Add("foo", new List<int>());

dictionary["foo"].Add(42);
dictionary["foo"].AddRange(oneHundredInts);

或者List&lt;string&gt;,就像你的情况一样。

顺便说一句,如果您知道要添加到动态集合中的项目数量,例如List&lt;T&gt;,请选择采用初始列表容量的构造函数:new List&lt;int&gt;(100);

这将获取满足指定容量所需的内存预先,而不是每次开始填满时都获取小块。如果你知道你有 100 个键,你可以对字典做同样的事情。

【讨论】:

  • 这(总是)需要 2 次查找。
  • 使用 TryGetValue 比 ContainsKey 和重新索引到字典中的性能更高。
  • @Roken 我知道,但这不是问题的症结所在。我也从未见过以这种方式使用字典而产生的任何有价值的性能问题。过早的优化或微优化。
  • @AdamHouldsworth 我们在纳秒级别对代码进行基准测试。仅仅因为您没有看到最有效解决方案的价值,并不意味着它对与您在不同领域工作的其他人没有价值。
  • @Roken 那么我看不出你为什么使用 C#、.NET 和你无法个人控制的 CLR。在这门语言中,我一点也不费神。不要将我的回答误解为没有看到价值——我只是更看重其他东西。
【解决方案2】:

如果我明白你想要什么:

dictionary.Add("key", new List<string>()); 

稍后……

dictionary["key"].Add("string to your list");

【讨论】:

    【解决方案3】:
    Dictionary<string, List<string>> dictionary = new Dictionary<string,List<string>>();
    
    foreach(string key in keys) {
        if(!dictionary.ContainsKey(key)) {
            //add
            dictionary.Add(key, new List<string>());
        }
        dictionary[key].Add("theString");
    }
    

    如果密钥不存在,则添加一个新的List(在 if 中)。否则键存在,所以只需在该键下的List 中添加一个新值。

    【讨论】:

      【解决方案4】:

      您可以使用我的多图实现,它派生自Dictionary&lt;K, List&lt;V&gt;&gt;。它并不完美,但它做得很好。

      /// <summary>
      /// Represents a collection of keys and values.
      /// Multiple values can have the same key.
      /// </summary>
      /// <typeparam name="TKey">Type of the keys.</typeparam>
      /// <typeparam name="TValue">Type of the values.</typeparam>
      public class MultiMap<TKey, TValue> : Dictionary<TKey, List<TValue>>
      {
      
          public MultiMap()
              : base()
          {
          }
      
          public MultiMap(int capacity)
              : base(capacity)
          {
          }
      
          /// <summary>
          /// Adds an element with the specified key and value into the MultiMap. 
          /// </summary>
          /// <param name="key">The key of the element to add.</param>
          /// <param name="value">The value of the element to add.</param>
          public void Add(TKey key, TValue value)
          {
              List<TValue> valueList;
      
              if (TryGetValue(key, out valueList)) {
                  valueList.Add(value);
              } else {
                  valueList = new List<TValue>();
                  valueList.Add(value);
                  Add(key, valueList);
              }
          }
      
          /// <summary>
          /// Removes first occurence of an element with a specified key and value.
          /// </summary>
          /// <param name="key">The key of the element to remove.</param>
          /// <param name="value">The value of the element to remove.</param>
          /// <returns>true if the an element is removed;
          /// false if the key or the value were not found.</returns>
          public bool Remove(TKey key, TValue value)
          {
              List<TValue> valueList;
      
              if (TryGetValue(key, out valueList)) {
                  if (valueList.Remove(value)) {
                      if (valueList.Count == 0) {
                          Remove(key);
                      }
                      return true;
                  }
              }
              return false;
          }
      
          /// <summary>
          /// Removes all occurences of elements with a specified key and value.
          /// </summary>
          /// <param name="key">The key of the elements to remove.</param>
          /// <param name="value">The value of the elements to remove.</param>
          /// <returns>Number of elements removed.</returns>
          public int RemoveAll(TKey key, TValue value)
          {
              List<TValue> valueList;
              int n = 0;
      
              if (TryGetValue(key, out valueList)) {
                  while (valueList.Remove(value)) {
                      n++;
                  }
                  if (valueList.Count == 0) {
                      Remove(key);
                  }
              }
              return n;
          }
      
          /// <summary>
          /// Gets the total number of values contained in the MultiMap.
          /// </summary>
          public int CountAll
          {
              get
              {
                  int n = 0;
      
                  foreach (List<TValue> valueList in Values) {
                      n += valueList.Count;
                  }
                  return n;
              }
          }
      
          /// <summary>
          /// Determines whether the MultiMap contains an element with a specific
          /// key / value pair.
          /// </summary>
          /// <param name="key">Key of the element to search for.</param>
          /// <param name="value">Value of the element to search for.</param>
          /// <returns>true if the element was found; otherwise false.</returns>
          public bool Contains(TKey key, TValue value)
          {
              List<TValue> valueList;
      
              if (TryGetValue(key, out valueList)) {
                  return valueList.Contains(value);
              }
              return false;
          }
      
          /// <summary>
          /// Determines whether the MultiMap contains an element with a specific value.
          /// </summary>
          /// <param name="value">Value of the element to search for.</param>
          /// <returns>true if the element was found; otherwise false.</returns>
          public bool Contains(TValue value)
          {
              foreach (List<TValue> valueList in Values) {
                  if (valueList.Contains(value)) {
                      return true;
                  }
              }
              return false;
          }
      
      }
      

      请注意,Add 方法会查看密钥是否已存在。如果键是新的,则创建一个新列表,将值添加到列表中,并将列表添加到字典中。如果键已经存在,则将新值添加到现有列表中。

      【讨论】:

      • 如果你打算把它带到这个抽象级别,为什么不使用Dictionary&lt;TKey, HashSet&lt;TValue&gt;&gt;。您只需对内部集合执行添加/删除/包含检查,这是 HashSet 的理想选择。
      • 语义略有不同。我的实现允许您为同一个键多次插入相同的值。我不知道这两个变体是否有不同的术语。 MultiMap 适用于哪一个?我的变体可能是MultiMap,您的变体可能是MultiSet
      • 我根本不会使用继承。此类的用户希望完全隐藏 Dictionary 界面。您要么想要 MultiMap 或 Dictionary,但不能两者兼得。
      • 这是一个快速的解决方案。当然,您可以从一个新类开始并实现IDictionary&lt;K,V&gt; 加上一些特定于多地图的东西以获得完美的解决方案。在内部,您将使用Dictionary&lt;K,List&lt;V&gt;&gt;。实现IDictionary&lt;K,V&gt; 需要实现16 个属性和方法。正如我在文章开头所写的,所提出的解决方案并不完美。
      • 此外,此实现允许您通过原始字典接口添加和检索整个列表。
      【解决方案5】:

      使用 NameValuedCollection。

      好的起点是here。直接来自链接。

      System.Collections.Specialized.NameValueCollection myCollection
          = new System.Collections.Specialized.NameValueCollection();
      
        myCollection.Add(“Arcane”, “http://arcanecode.com”);
        myCollection.Add(“PWOP”, “http://dotnetrocks.com”);
        myCollection.Add(“PWOP”, “http://dnrtv.com”);
        myCollection.Add(“PWOP”, “http://www.hanselminutes.com”);
        myCollection.Add(“TWIT”, “http://www.twit.tv”);
        myCollection.Add(“TWIT”, “http://www.twit.tv/SN”);
      

      【讨论】:

      • 1.这是一个 NameValueCollection - 没有 'd' 和 2。请注意,您应该使用 GetValues(String) 而不是索引器 - 索引器返回一个逗号分隔的字符串以及您的值,如果您的值可能包含逗号和 3,这将是有问题的。该集合不区分 null 作为 value 或 null 作为 key-not-found
      【解决方案6】:

      虽然与大多数其他响应几乎相同,但我认为这是实现它的最有效和最简洁的方式。正如其他一些解决方案所示,使用 TryGetValue 比使用 ContainsKey 和重新索引到字典中更快。

      void Add(string key, string val)
      {
          List<string> list;
      
          if (!dictionary.TryGetValue(someKey, out list))
          {
             values = new List<string>();
             dictionary.Add(key, list);
          }
      
          list.Add(val);
      }
      

      【讨论】:

        【解决方案7】:

        当你添加一个字符串时,根据键是否已经存在而有所不同。为键 key 添加字符串 value

        List<string> list;
        if (dictionary.ContainsKey(key)) {
          list = dictionary[key];
        } else {
          list = new List<string>();
          dictionary.Add(ley, list);
        }
        list.Add(value);
        

        【讨论】:

          【解决方案8】:

          为什么不使用字典,而不是转换为 ILookup?

          var myData = new[]{new {a=1,b="frog"}, new {a=1,b="cat"}, new {a=2,b="giraffe"}};
          ILookup<int,string> lookup = myData.ToLookup(x => x.a, x => x.b);
          IEnumerable<string> allOnes = lookup[1]; //enumerable of 2 items, frog and cat
          

          ILookup 是一种不可变的数据结构,它允许每个键有多个值。如果您需要在不同的时间添加项目,可能没有多大用处,但如果您预先准备好所有数据,那么这绝对是要走的路。

          【讨论】:

          • 谢谢。我需要在不同的时间添加项目。
          【解决方案9】:

          这是一个答案的许多变体:)我是另一个答案,它使用扩展机制作为舒适的执行方式(方便):

          public static void AddToList<T, U>(this IDictionary<T, List<U>> dict, T key, U elementToList)
          {
          
              List<U> list;
          
              bool exists = dict.TryGetValue(key, out list);
          
              if (exists)
              {
                  dict[key].Add(elementToList);
              }
              else
              {
                  dict[key] = new List<U>();
                  dict[key].Add(elementToList);
              }
          
          }
          

          那你按如下方式使用:

          Dictionary<int, List<string>> dict = new Dictionary<int, List<string>>();
          
          dict.AddToList(4, "test1");
          dict.AddToList(4, "test2");
          dict.AddToList(4, "test3");
          
          dict.AddToList(5, "test4");
          

          【讨论】:

            【解决方案10】:

            有一个 NuGet 包 Microsoft Experimental Collections,其中包含一个类 MultiValueDictionary,它完全可以满足您的需求。

            Here 是该包的创建者的博客文章,对其进行了进一步描述。

            Here 是另一篇博文,如果您感到好奇的话。

            示例用法:

            MultiDictionary<string, int> myDictionary = new MultiDictionary<string, int>();
            myDictionary.Add("key", 1);
            myDictionary.Add("key", 2);
            myDictionary.Add("key", 3);
            //myDictionary["key"] now contains the values 1, 2, and 3
            

            【讨论】:

              【解决方案11】:

              我试图将 List 添加到字典中的现有键并达到以下解决方案:

              Dictionary<string,List<string>> NewParent = new Dictionary<string,List<string>>();
              child = new List<string> ();
              child.Add('SomeData');
              NewParent["item1"].AddRange(child);
              

              它不会显示任何异常,也不会替换以前的值。

              【讨论】:

                【解决方案12】:

                使用来自ConcurrentDictionaryAddOrUpdate 有一种“单命令行”方式来执行此操作:

                using System.Linq;
                using System.Collections.Generic;
                using System.Collections.Concurrent;
                 
                ...
                
                var dictionary = new ConcurrentDictionary<string, IEnumerable<string>>();
                var itemToAdd = "item to add to key-list";
                
                dictionary.AddOrUpdate("key", new[]{itemToAdd}, (key,list) => list.Append(itemToAdd));
                
                // If "key" doesn't exist, creates it with a list containing itemToAdd as value
                // If "key" exists, adds item to already existent list (third parameter)
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-03-31
                  • 2014-06-27
                  相关资源
                  最近更新 更多