【问题标题】:using long (int64) as a hashCode and still use IEqualityComparer for concurrent Dictionary使用 long (int64) 作为 hashCode 并且仍然使用 IEqualityComparer 进行并发 Dictionary
【发布时间】:2014-04-18 18:52:00
【问题描述】:

我在并发字典中使用自制的 IEqualityComparer 和 GetHashCode 时遇到问题。

当我这样实现时,下面的类(使用两个属性进行了简化)非常完美:

ConcurrentDictionary<TwoUintsKeyInfo,Int64> hashCodePlusIandJDict = new ConcurrentDictionary<TwoUintsKeyInfo, Int64>();

.

public class TwoUintsKeyInfo
{
    public uint IdOne { get; set; }
    public uint IdTwo { get; set; }

    #region Implemetation of the IEqualityComparer

    public class EqualityComparerTwoUintsKeyInfo : IEqualityComparer<TwoUintsKeyInfo>
    {
        System.Reflection.PropertyInfo[] properties;
        bool propertyArraySet=false;

        public int GetHashCode(TwoUintsKeyInfo obj)
        {
            unchecked
            {
                if(!propertyArraySet)
                {
                    properties = obj.GetType().GetProperties().OrderBy(x => x.Name).ToArray();
                    propertyArraySet = true;
                }

                decimal hash = 17;
                int counter=0;
                foreach(System.Reflection.PropertyInfo p in properties)
                {
                    counter++;
                    var value = p.GetValue(obj);
                    decimal unique = (decimal)Math.Pow(Math.E, counter);
                    hash = hash + (value == null ? unique : value.GetHashCode() * unique);
                }
                return 2147483647M * .001M > hash ? (int)(hash * 1000) : (int)hash;
            }
        }

        public bool Equals(TwoUintsKeyInfo x, TwoUintsKeyInfo y)
        {
            return GetHashCode(x) == GetHashCode(y);
        }
    }

    #endregion Implemetation of the IEqualityComparer
}

现在我做了几乎相同的类,但是我做了一点改变,而不是普通的IEqualityComparer接口,所以我可以生成long / int64 hascodes(因为当类持有越来越多的属性时,我们遇到了多个值与相同的哈希码)

所以我想减少获得相同hascode的变化。因此,我想使用更大的数字,如果可能的话,乘以 10000 来获得一些小数。

因此我创建了这个界面:

public interface IEqualityComparerInt64<in T>
{
    bool Equals(T x, T y);
    Int64 GetHashCode(T obj);
}

并更改了属性类,使其看起来像这样:

public class TwoUintsKeyInfoInt64
{
    public uint IdOne { get; set; }
    public uint IdTwo { get; set; }

    #region Implemetation of the IEqualityComparer

    public class EqualityComparerTwoUintsKeyInfoInt64 : IEqualityComparerInt64<TwoUintsKeyInfoInt64>
    {
        System.Reflection.PropertyInfo[] properties;
        bool propertyArraySet=false;
        decimal _upperThreshold,_lowerThreshold;

        public EqualityComparerTwoUintsKeyInfoInt64()
        {
            _upperThreshold = long.MaxValue * .0001M;
            _lowerThreshold = -long.MaxValue * .0001M;
        }

        public long GetHashCode(TwoUintsKeyInfoInt64 obj)
        {
            unchecked
            {
                if(!propertyArraySet)
                {
                    properties = obj.GetType().GetProperties().OrderBy(x => x.Name).ToArray();
                    propertyArraySet = true;
                }

                decimal hash = 17;
                int counter=0;
                foreach(System.Reflection.PropertyInfo p in properties)
                {
                    counter++;
                    var value = p.GetValue(obj);
                    decimal unique = (decimal)Math.Pow(Math.E, counter);
                    hash = hash + (value == null ? unique : value.GetHashCode() * unique);
                }
                return _upperThreshold > hash && _lowerThreshold < hash ? (long)(hash * 10000) : (long)hash;
            }
        }

        public bool Equals(TwoUintsKeyInfoInt64 x, TwoUintsKeyInfoInt64 y)
        {
            return GetHashCode(x) == GetHashCode(y);
        }
    }

    #endregion Implemetation of the IEqualityComparer
}

GetHashCode 工作正常。目前没问题。

但是...当我尝试像这样将 IEqualityComparer 添加到并发字典时:

    ConcurrentDictionary<TwoUintsKeyInfoInt64,Int64> hashCodePlusIandJDict = new ConcurrentDictionary<TwoUintsKeyInfoInt64, Int64>(new TwoUintsKeyInfoInt64.EqualityComparerOneUintAndTwoStringKeyInfo());

我收到此错误:

错误 3 参数 1:无法从 'HasCodeTestForUniqueResult.TwoUintsKeyInfoInt64.EqualityComparerOneUintAndTwoStringKeyInfo' 到 'System.Collections.Generic.IEqualityComparer' D:\Users\mldz\Documents\visual 工作室 2012\HashCodeTestForUniqueResult\HashCodeTestForUniqueResult\Form1.cs 109 140 HashCodeTestForUniqueResult

我了解默认 System.Collections.Generic.IEqualityComparer 的 int 类型与我自己的 GetHashCode 生成器的 long / int64 结果之间存在冲突。但是有什么办法可以解决这个问题并能够使用长哈希码?

亲切的问候,

马蒂斯

附:上面的代码只是为了测试它并复制问题。

【问题讨论】:

  • 为什么要长期创建自己的数据类型? (参考 TwoUintsKeyInfoInt64)
  • 什么意思?我想让 GetHashCode 更精确(阅读:不同值具有相同哈希码的变化更少)
  • 附带说明,这不好:public bool Equals(TwoUintsKeyInfo x, TwoUintsKeyInfo y) { return GetHashCode(x) == GetHashCode(y); }。正如您自己指出的那样,相同的哈希码并不一定意味着这两个对象是相同的。只有当两个对象真正相同时,Equal 才应该返回 true
  • 是的,这是一个修复,只是为了实现 Iequalitycomparer。
  • 你为什么在这里使用反射?为什么不直接访问这两个属性呢?关于“更精确”,您知道这将是一个问题吗?您是否了解值分布以及这如何影响哈希码值?

标签: c# hash gethashcode iequalitycomparer concurrentdictionary


【解决方案1】:

根据this,你不能使用长哈希码,所以这个问题的答案是否定的。

但是你可以有唯一的组合而不是唯一的值;解决方案是实现一个分区系统,意思是有一个字典字典,比如:

public class MyClass 
{
    Dictionary<uint, Dictionary<uint, Int64>> PartDict;

    Int64 ReadValue(uint id1, uint id2)
    {
        return (PartDict[id1])[id2];
    }

    void AddValue(uint id1, uint id2, Int64 value)
    {
        Dictionary<uint, Int64> container;
        if (!PartDict.TryGetValue(id1, out container))
        {
            container = new Dictionary<uint, Int64>();
            PartDict.Add(id1, container);
        }
        container.Add(id2, value);
    }
}

这样,您将拥有一个哈希码列表,并且每个哈希码将再次拥有一个哈希码列表,组合是唯一的。不过,任何读取和写入都将分两步完成(以考虑是否需要唯一的哈希来提高性能)。

希望对你有帮助。

【讨论】:

  • 嗨,我实际上实现了一个类似的解决方案来让它工作。这似乎没问题。所以,虽然我已经有了这样的东西;检查答案 ;-) 感谢您的回复
猜你喜欢
  • 2011-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-29
  • 1970-01-01
  • 1970-01-01
  • 2018-01-13
  • 1970-01-01
相关资源
最近更新 更多