【问题标题】:Get KeyValuePair by Key from a ConcurrentDictionary (in O(1) time)从 ConcurrentDictionary 中按键获取 KeyValuePair(在 O(1) 时间内)
【发布时间】:2020-04-07 17:02:04
【问题描述】:

根据此解决方案 (https://stackoverflow.com/a/18923091/529618),我使用 ConcurrentDictionary<T,byte> 作为缺少 ConcurrentHashSet<T> 的解决方法。但是,我很想知道如何在 O(1) 时间内从字典中取出原始的 T Key

var cache = new ConcurrentDictionary<MyEquatableClass, byte>());
//...
if(!cache.TryAdd(classInstance, Byte.MinValue))
    return /* Existing cache entry */;
return classInstance;

有没有办法通过给它一个等效的(IEquatable)键来获取KeyValuePair&lt;K,V&gt;(甚至只是键)的ConcurrentDictionary&lt;K,V&gt;条目,而不用在O(n)时间内枚举它?

我的问题出现是因为我用作键的对象是IEquatable&lt;K&gt; 到另一个,而不是ReferenceEqual 到另一个。如果myDict.ContainsKey(someEquatable),我想获取字典中的原始键实例(以及与它一起存储的值),并丢弃我当前的(重复)实例。

【问题讨论】:

  • 你看过ConcurrentDictionary.GetOrAdd方法吗?是否也可以选择使用自定义 IEqualityComparer 而不是依赖类型的平等性?
  • 尝试检索您用作键的实际对象是一种不可靠的想法。 HashSet 也不会真正让你这样做。正如您所发现的,解决方案是将键对象存储为值。如果值是引用类型,那么内存占用不会比使用byte 时大。
  • 阿兰你是对的,我误解了你的问题。关于劫持自定义相等比较器的Equals 方法,不,我不认为我会建议。这听起来太骇人听闻了。 :-)
  • @JimMischel 不是 sizeof(byte) (1) 小于 IntPtr.Size (4-8) 吗?还请查看HashSet.TryGetValue 方法:在集合中搜索给定值并返回它找到的相等值(如果有)。
  • @JimMischel 你是对的!我刚刚测试了它,Dictionary&lt;MyClass,byte&gt;Dictionary&lt;MyClass,MyClass&gt; 具有相同的内存占用(具有相同数量的项目)。

标签: c# data-structures concurrency concurrentdictionary


【解决方案1】:

我刚刚意识到我可以从使用ConcurrentDictionary&lt;TKey, byte&gt; 切换到ConcurrentDictionary&lt;TKey, TKey&gt;。它可能比字节值(未确认)具有更大的占用空间,但如果值和键相同,我可以轻松地从值中获取键。

要将其扩展到那些发现此问题并且实际使用“值”的人,您可以选择将您的字典更改为 ConcurrentDictionary&lt;TKey, Tuple&lt;TKey, TValue&gt;,并以这种方式获取原始键和值。

var cache = new ConcurrentDictionary<MyEquatableClass, MyEquatableClass>());
//...
if(!cache.TryAdd(classInstance, classInstance))
    return cache[classInstance];
return classInstance;

【讨论】:

    【解决方案2】:

    这是一种将值添加到ConcurrentDictionary&lt;T, T&gt; 的扩展方法,该方法用作ConcurrentHashSet&lt;T&gt;(具有等于键的值):

    /// <summary>
    /// Adds a value to a <see cref="ConcurrentDictionary{T,T}"/>
    /// used as a concurrent <see cref="HashSet{T}"/>, if it does not already exist.<br/>
    /// Returns the new value, or the existing value if the value exists.
    /// </summary>
    /// <param name="value">The value to be added, if it does not already exist.</param>
    public static T GetOrAdd<T>(this ConcurrentDictionary<T, T> source, T value)
    {
        return source.GetOrAdd(value, value);
    }
    

    使用示例:

    var dict = new ConcurrentDictionary<string, string>(StringComparer.OrdinalIgnoreCase);
    Console.WriteLine($"dict.GetOrAdd(\"abc\"): {dict.GetOrAdd("abc")}");
    Console.WriteLine($"dict.GetOrAdd(\"ABC\"): {dict.GetOrAdd("ABC")}");
    Console.WriteLine($"dict.Count: {dict.Count}");
    

    输出:

    dict.GetOrAdd("abc"): abc
    dict.GetOrAdd("ABC"): abc
    dict.Count: 1
    

    【讨论】:

      猜你喜欢
      • 2020-09-22
      • 2016-10-04
      • 2017-07-30
      • 1970-01-01
      • 2012-08-16
      • 2011-04-09
      • 1970-01-01
      • 2021-08-10
      • 2016-12-31
      相关资源
      最近更新 更多