替代日益慢的Dictionary.Add(Key,Value)?

时间:2015-08-04 13:53:38

标签: c# dictionary

越来越多"我的意思是当密钥数量很少时Add在开始时很快。插入20%的键后,它变得非常慢。在50%之后,它变得难以忍受地缓慢。

我得到的是键数越少,键碰撞搜索越快"在向字典中添加新元素时。但有没有办法在保持Dictionary的同时跳过这个缺点?我事先知道密钥不会碰撞,因此不需要检查,但我不知道是否有任何方法可以在代码中成功使用此信息。

BTW由于体系结构的限制,我不得不使用字典结构(这个结构后来被db导出器吞噬了。)

我的代码的作用:

var keyList = GetKeyList();
var resultDict = new Dictionary<T,T>();
foreach (var key in keyList)
{
    resultDict.Add(key,someResult);
}

编辑,因为人们在询问如何生成哈希码,我会尝试澄清这一点。

理论上我无法控制哈希码生成,因为不幸的是它使用了通过同一个数据库连接的多个系统之间的约定。

实际上,生成哈希码的代码确实是我的代码(免责声明:我没有选择代码中使用的约定。)

密钥生成比这更复杂,但这一切归结为:

private List<ResultKey> GetKeyList(string prefix, List<float> xCoordList, List<float> yCoordList)
{
    var keyList = new List<ResultKey>();
    var constantSensorName = "xxx";
    foreach (float xCoord in xCoordList)
    {
        foreach (float yCoord in yCoordList)
        {
            string stationName = string.Format("{0}_E{1}N{2}", prefix, xCoord, yCoord);
            keyList.Add(new ResultKey(constantSensorName, stationName));
        }
    }
    return keyList;
}

public struct ResultKey
{
    public string SensorName { get; set; }
    public string StationName { get; set; }

    public ResultKey(string sensorName, string stationName)
    {
        this.SensorName = sensorName;
        this.StationName = stationName;
    }
}

4 个答案:

答案 0 :(得分:6)

首先想到的是创建自己的散列函数。当将字典添加到结构时,字典的Add方法将调用getHashCode()方法的默认实现。如果你在你的密钥周围放置一个包装类并覆盖了getHashCode()方法,那么你可以编写自己的散列函数,这可能会实现一个不易发生碰撞的散列函数。

答案 1 :(得分:2)

您正在使用结构ResultKey的默认哈希码生成。 The default hash code generation for structs is disappointingly bad.你不能在这里依赖它,因为你的struct包含两个触发坏情况的字符串(参见链接的答案)。从本质上讲,只有您的SensorName字段才能将其作为哈希码,而不是其他内容。这会导致具有相同SensorName的所有密钥发生冲突。

编写自己的功能。我使用Resharper快速生成了一个:

public struct ResultKey : IEquatable<ResultKey>
{
    public string SensorName { get; set; }
    public string StationName { get; set; }

    public ResultKey(string sensorName, string stationName)
    {
        this.SensorName = sensorName;
        this.StationName = stationName;
    }

    public bool Equals(ResultKey other)
    {
        return string.Equals(SensorName, other.SensorName) && string.Equals(StationName, other.StationName);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        return obj is ResultKey && Equals((ResultKey)obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return ((SensorName != null ? SensorName.GetHashCode() : 0)*397) ^ (StationName != null ? StationName.GetHashCode() : 0);
        }
    }

    public static bool operator ==(ResultKey left, ResultKey right)
    {
        return left.Equals(right);
    }

    public static bool operator !=(ResultKey left, ResultKey right)
    {
        return !left.Equals(right);
    }
}

答案 2 :(得分:1)

您的ResultKey包含两个字符串,因此您需要一个组合它们的哈希码。

How do I calculate a good hash code for a list of strings?”包含一些显示如何执行此操作的答案。

然而,你做得差得多

public override int GetHashCode()
{   
   return (SensorName + StationName).GetHashCode();
}

答案 3 :(得分:-2)

如果您只想满足API要求并需要一个肮脏的解决方案,您可以实现自己的词典。

public class FakeFastDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
    protected IList<KeyValuePair<TKey, TValue>> _list
        = new List<KeyValuePair<TKey, TValue>>();

    public new void Add(TKey key, TValue value)
    {
        _list.Add(new KeyValuePair<TKey, TValue>(key, value));
    }

    public new ICollection<TValue> Values
    {

        get
        {
            // there may be faster ways to to it:
            return _list.Select(x => x.Value).ToArray();
        }
    }

    public new ICollection<TKey> Keys
    {
        get
        {
            // there may be faster ways to to it:
            return _list.Select(x => x.Key).ToArray();
        }
    }
}

这是一个正在运行的示例: https://dotnetfiddle.net/BDyks0