【问题标题】:ConcurrentDictionary adding same keys more than onceConcurrentDictionary 多次添加相同的键
【发布时间】:2015-01-17 02:00:48
【问题描述】:

我想用ConcurrentDictionary来检查这个数据键之前是否已经添加过,但是看起来我仍然可以添加之前添加的键。

代码:

    public class pKeys
    {
        public pKeys()
        { }
        public pKeys(long sID, long pID)
        {
            this.seID = sID;
            this.pgID = pID;

        }
        public long seID;
        public long pgID;
    }

    public static ConcurrentDictionary<pKeys, bool> existenceDic 
= new ConcurrentDictionary<pKeys, bool>();

测试代码:

    pKeys temKey = new pKeys(111, 222);
    bool res = existenceDic.TryAdd(temKey, true);
    Console.WriteLine(res);

    temKey = new pKeys(111, 222);
    res = existenceDic.TryAdd(temKey, true);
    Console.WriteLine(res);

结果:

true
true

【问题讨论】:

  • 您的pKeys 对象不会覆盖GetHashCode()Equals(),因此这两个不同的实例被视为不同的值。要么使用同一个实例两次(而不是第二次调用new),要么正确覆盖这些方法,以便包含相同值的不同实例被认为是相同的值。
  • 也许很容易解决,我只需将两个元素键转换为字符串,然后像哈希一样工作......
  • 不,不要那样做。如果你这样做,那么 (11, 1222) 的值将与 (111, 222) 的值具有相同的哈希码,这没有任何理由。碰撞不是致命的,但您应该设计哈希码实现,至少尽量避免它们。
  • 好吧,我在做"111"+"="+"222"
  • "111"+"="+"222" 仍然是错误的。它仅解决了我用该特定方法指出的三个具体问题之一(坦率地说,是最不重要的一个)。

标签: c# dictionary concurrency parallel-processing concurrentdictionary


【解决方案1】:

您可以添加两个包含相同值的不同实例,因为您尚未覆盖 GetHashCode()Equals()。这会导致使用默认的相等比较,对于引用类型,它只是比较引用本身。在这种情况下,两个不同的实例总是被视为不同的值。

一种选择是将您的类型设为struct 而不是class。这使用将考虑字段值的默认比较。

或者,您可以继续覆盖GetHashCode()Equals()。例如:

public class pKeys
{
    public pKeys()
    { }
    public pKeys(long sID, long pID)
    {
        this.seID = sID;
        this.pgID = pID;

    }
    public readonly long seID;
    public readonly long pgID;

    public override int GetHashCode()
    {
        return seID.GetHashCode() * 37 + pgID.GetHashCode();
    }

    public override bool Equals(object other)
    {
        pKeys otherKeys = other as pKeys;

        return otherKeys != null &&
            this.seID == otherKeys.seID &&
            this.pgID == otherKeys.pgID;
    }
}

注意事项:

  • 哈希码是根据各个值的哈希码计算的。 1 乘以 37,这只是一个方便的素数;有些人更喜欢使用更大的素数来更好地“混合”。在大多数情况下,以上方法都能正常工作,恕我直言。
  • 请注意,您提出的解决方案(将值转换为字符串、将它们连接起来并返回其哈希码)有几个不利方面:
    • 您必须创建三个字符串实例才能生成哈希码!单独的内存开销已经足够糟糕了,但当然还有格式化两个整数的成本。
    • 从字符串生成哈希码比从整数值更昂贵
    • 发生冲突的风险要高得多,因为不同的值更容易产生相同的字符串(例如 (11, 2222) 和 (111, 222))
  • 我在您的字段中添加了readonly。如果您决定将类型设为 struct(即即使您不覆盖方法),这将是关键。但即使对于一个类,可相等的可变类型也是一个大问题,因为如果它们在添加到基于散列的集合后发生更改,则该集合实际上被破坏了。在此处使用readonly 可确保类型是不可变的。 (此外,恕我直言,应该避免使用公共字段,但如果必须拥有它们,它们肯定应该是 readonly,即使您不覆盖相等方法。
  • 有些人喜欢在Equals() 方法中检查确切的类型相等性。事实上,这通常是一个好主意……它简化了比较对象的场景并使代码更易于维护。但是为了举例,可分配性(即as)更容易阅读,而且在许多情况下都是有效的。

请参阅General advice and guidelines on how to properly override object.GetHashCode() 以获得更多指导。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-08-18
    • 2012-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多