【问题标题】:Remove from Dictionary by Key and Retrieve Value按键从字典中删除并检索值
【发布时间】:2013-04-08 05:14:13
【问题描述】:

有没有办法从DictionaryKey)中删除一个条目并在同一步骤中检索其Value

例如,我在打电话

Dictionary.Remove(Key);

但我也希望它同时返回值。该函数只返回一个bool

我知道我可以做类似的事情

Value = Dictionary[Key];
Dictionary.Remove(Key);

但这似乎会搜索字典两次(一次获取值,另一次从字典中删除它)。我怎样才能(如果可能)在不搜索字典两次的情况下同时进行这两项操作?

【问题讨论】:

  • stackoverflow.com/questions/15785091/… 的可能重复项此主题的答案已被接受
  • 如果它也与性能有关,您可以在这里查看答案stackoverflow.com/questions/1869452/…,看看是否可以解决任何问题
  • @Kenneth:所以,我想这不能用 .NET 中的“内置”字典类型来完成?
  • @Mash: 假设你的Key 缓存或者平凡计算哈希码,双重查找的性能成本是微不足道的。大多数字典都有 O(1) 的查找和删除。
  • afaik,不,但既然这是一个如此明显和合法的要求,我想以前一定有人问过它。我想唯一可能的方法是实现你自己的字典。不重要,但我怀疑它会更快:)

标签: c# .net dictionary


【解决方案1】:

【讨论】:

【解决方案2】:

因为它们都有所需的缺失方法,所以我尝试了 Microsoft 的 ConcurrentDictionary 和哥本哈根大学的 C5 http://www.itu.dk/research/c5/,我可以看出,至少在我的用例中它非常慢(我的意思是慢 5 倍 - 10 倍)与字典相比。 我认为 C5 一直在对键和值进行排序,并发字典对调用线程“太担心”了。我不是在这里讨论为什么字典的这两个化身很慢。 我的算法正在寻找和替换一些条目,而第一个键将被删除并添加新键(某种队列)...... 唯一要做的就是修改原始.Net mscorelib 的字典。我从 Microsoft 下载了源代码,并在我的源代码中包含了 Dictionary 类。要编译,我还需要拖动 HashHelpers 类和 ThrowHelper 类。剩下的就是注释掉一些行(例如[DebuggerTypeProxy(typeof(Mscorlib_DictionaryDebugView<,>))] 和一些资源获取)。显然我必须将缺少的方法添加到复制的类中。也不要尝试编译 Microsoft 源代码,你将这样做几个小时,我很幸运能够让它继续下去。

   public bool Remove(TKey key, out TValue value)
    {
        if (key == null)
        {
            ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
        }

        if (buckets != null)
        {
            int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
            int bucket = hashCode % buckets.Length;
            int last = -1;
            for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next)
            {
                if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key))
                {
                    if (last < 0)
                    {
                        buckets[bucket] = entries[i].next;
                    }
                    else
                    {
                        entries[last].next = entries[i].next;
                    }
                    entries[i].hashCode = -1;
                    entries[i].next = freeList;
                    entries[i].key = default(TKey);
                    value = entries[i].value;
                    entries[i].value = default(TValue);
                    freeList = i;
                    freeCount++;
                    version++;
                    return true;
                }
            }
        }
        value = default(TValue);
        return false;
    }

最后我将命名空间修改为System.Collection.Generic.My 在我的算法中,我只有两行获得值,而不是在下一行中删除它。用 new 方法替换它,并获得了 7%-10% 的稳定性能增益。 希望它对这个用例以及从头重新实现字典不是应该做的任何其他情况有所帮助。

【讨论】:

  • 所以基本上,修改微软的库。这个问题已经有一段时间了,这个答案似乎已经很好了,所以我会接受这个解决方案。谢谢!
【解决方案3】:

尽管这不是 OP 所要求的,但我还是忍不住发布了一个更正的扩展方法:

public static bool Remove<TKey, TValue>(this Dictionary<TKey, TValue> self, TKey key, out TValue target)
{
    self.TryGetValue(key, out target);
    return self.Remove(key);
}

【讨论】:

    【解决方案4】:

    concurrentDictionary 有一个TryRemove 方法,该方法尝试从System.Collections.Concurrent.ConcurrentDictionary&lt;TKey, TValue&gt;. 中删除并返回具有指定键的值 如果key不存在,则返回TValue类型的默认值。

    https://msdn.microsoft.com/en-us/library/dd287129(v=vs.110).aspx

    【讨论】:

      【解决方案5】:

      您可以使用扩展方法来做到这一点:

      public static string GetValueAndRemove<TKey, TValue>(this Dictionary<int, string> dict, int key)
      {
          string val = dict[key];
          dict.Remove(key);
          return val;    
      }
      
      static void Main(string[] args)
      {
          Dictionary<int, string> a = new Dictionary<int, string>();
          a.Add(1, "sdfg");
          a.Add(2, "sdsdfgadfhfg");
          string value = a.GetValueAndRemove<int, string>(1);
      }
      

      【讨论】:

      • 由于删除是安全的,我建议先检查密钥是否存在,但无论如何都是个好主意:)
      • 感谢您的回复,但是当我说“在同一步骤中”时,我应该更清楚。我真正的意思是不要两次搜索字典。 string val = dict[key] 将在字典中搜索一次,dict.Remove(key) 将再次搜索。
      • @Mash Dictionary 真的很快,所以性能方面应该不是什么大问题。
      • @Silvermind 我同意,这真的不是性能问题,只是我在编写代码时的一个想法
      • 如果您在哥本哈根大学的通用集合图书馆中查看字典的引擎盖下,您可能会发现相同的实现。不幸的是,您不能一步完成。
      【解决方案6】:

      您可以扩展该类以添加该功能:

      public class PoppableDictionary<T, V> : Dictionary<T, V>
      {
          public V Pop(T key)
          {
              V value = this[key];
              this.Remove(key);
              return value;
          }
      }
      

      【讨论】:

      • 这仍然需要两次查找。 OP 要求一种消除双重查找的方法
      • 你是对的。我根据@Guvante 的评论进行了回答,该评论指出对字典的访问操作为 o(1),因此两次访问字典不会显着降低性能。
      • @Toto 是的,我主要是想避免在字典中重复查找,但感谢您提供了一些东西,我至少可以在一次通话中做到这一点(upvote)。
      猜你喜欢
      • 2019-06-25
      • 2012-03-18
      • 1970-01-01
      • 2021-09-20
      • 1970-01-01
      • 2016-03-28
      • 1970-01-01
      • 2013-06-22
      • 1970-01-01
      相关资源
      最近更新 更多