C# 替代日益缓慢的字典。添加(键、值)?

C# 替代日益缓慢的字典。添加(键、值)?,c#,dictionary,C#,Dictionary,我所说的“越来越多”的意思是,当钥匙数量较少时,Add在开始时速度很快。插入20%的键后,速度会非常慢。50%之后,速度会慢得令人无法忍受 我发现,在向字典添加新元素时,键的数量越少,“键冲突搜索”越快。但是,在保留词典的同时,有没有可能跳过这一缺点呢?我事先知道键不会碰撞,因此不需要检查,但我不知道是否有任何方法可以成功地在代码中使用此信息 顺便说一句,由于架构限制,我不得不使用字典结构(这个结构后来被db导出器吞并) 我的代码的作用是: var keyList = GetKeyList()

我所说的“越来越多”的意思是,当钥匙数量较少时,
Add
在开始时速度很快。插入20%的键后,速度会非常慢。50%之后,速度会慢得令人无法忍受


我发现,在向字典添加新元素时,键的数量越少,“键冲突搜索”越快。但是,在保留
词典的同时,有没有可能跳过这一缺点呢?我事先知道键不会碰撞,因此不需要检查,但我不知道是否有任何方法可以成功地在代码中使用此信息

顺便说一句,由于架构限制,我不得不使用字典结构(这个结构后来被db导出器吞并)


我的代码的作用是:

var keyList = GetKeyList();
var resultDict = new Dictionary<T,T>();
foreach (var key in keyList)
{
    resultDict.Add(key,someResult);
}
var keyList=GetKeyList();
var resultDict=新字典();
foreach(键列表中的var键)
{
resultdct.Add(key,someResult);
}

编辑:由于人们在询问哈希代码是如何生成的,我将尝试澄清这一点

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

实际上,生成哈希代码的代码片段实际上是我的代码(免责声明:不是我选择了生成中使用的约定)

密钥生成要复杂得多,但归根结底是这样的:

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;
    }
}
private List GetKeyList(字符串前缀、List xCoordList、List yCoordList)
{
var keyList=新列表();
var constantSensorName=“xxx”;
foreach(在xCoordList中浮动xCoord)
{
foreach(在yCoordList中浮动yCoord)
{
string stationName=string.Format(“{0}{1}N{2}”,前缀,xCoord,yCoord);
添加(新的结果键(constantSensorName,stationName));
}
}
返回键列表;
}
公共结构结果键
{
公共字符串SensorName{get;set;}
公共字符串StationName{get;set;}
公共结果键(字符串sensorName、字符串stationName)
{
this.SensorName=SensorName;
this.StationName=StationName;
}
}

首先想到的是创建自己的哈希函数。字典的Add方法将调用getHashCode()方法的默认实现,然后将其添加到结构中。如果您在键周围放置一个包装类并重写getHashCode()方法,那么您可以编写自己的哈希函数,据推测,该函数可以实现一个不太容易发生冲突的哈希函数。

如果您只想满足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();
        }
    }
}
公共类FakeFastDictionary:字典
{
受保护的IList\u列表
=新列表();
公共新空添加(TKey key,TValue value)
{
_添加(新的KeyValuePair(key,value));
}
公共新ICollection值
{
得到
{
//可能有更快的方法来实现这一点:
return _list.Select(x=>x.Value).ToArray();
}
}
公共新ICollection密钥
{
得到
{
//可能有更快的方法来实现这一点:
return _list.Select(x=>x.Key).ToArray();
}
}
}
这是一个正在运行的示例:

您正在为结构
ResultKey
使用默认哈希代码生成。您不能依赖于此,因为您的结构包含两个字符串,这会触发坏情况(请参阅链接的答案)。本质上,只有您的
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);
    }
}
public struct ResultKey:IEquatable
{
公共字符串SensorName{get;set;}
公共字符串StationName{get;set;}
公共结果键(字符串sensorName、字符串stationName)
{
this.SensorName=SensorName;
this.StationName=StationName;
}
公共布尔等于(结果键其他)
{
返回string.Equals(SensorName,other.SensorName)和&string.Equals(StationName,other.StationName);
}
公共覆盖布尔等于(对象对象对象)
{
if(ReferenceEquals(null,obj))返回false;
返回obj为ResultKey&&Equals((ResultKey)obj);
}
公共覆盖int GetHashCode()
{
未经检查
{
返回((SensorName!=null?SensorName.GetHashCode():0)*397)^(StationName!=null?StationName.GetHashCode():0);
}
}
公共静态布尔运算符==(结果键左,结果键右)
{
返回左。等于(右);
}
公共静态布尔运算符!=(结果键左,结果键右)
{
返回!左。等于(右);
}
}

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

“”包含一些说明如何执行此操作的答案

然而你会做得更糟

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

此外,如果OP不能控制要添加的类,他可以为字典构造函数指定一个IEqualityComparer。我确信关于哈希代码的想法是正确的,但我发现等待OP发布更完整的信息是一种更好的方法。我们还没有看到生成哈希代码的代码。您的密钥是什么?可能是错误的哈希代码生成器。“我事先知道密钥不会冲突,因此不需要检查,但我不知道是否有任何方法可以成功地在代码中使用此信息。”你怎么知道的?你是否尝试指定一个较大的初始容量以避免重新哈希?@usr每个密钥都是一个包含一对字符串的
struct
。这些字符串稍后是db导出器查找必须插入数据的位置所必需的。在这种特殊情况下,每个键的字符串都很长并且非常相似