【问题标题】:How to speed up search in a huge dictionary如何在庞大的字典中加快搜索速度
【发布时间】:2013-07-14 00:13:29
【问题描述】:

我有一本很大的字典,里面的内容是这样的:
(字典中不包含标题)

(code)        (names)
------------------------------
910235487     Diabetes, tumors, sugar sick, .....

我的字典中有超过 150K 行这种对。

用户输入的是关键词(诊断名称),我无法按键搜索字典。

代码如下:

var relevantIDs = this.dic.Where(ele => ele.Value.Contains(keyword)).Select(n => Convert.ToUInt64(n.Key));

字典是Dictionary<string, string>,我必须使用字符串作为键的数据类型,因为代码有时可以包含字符。名称列包含相关诊断名称的列表。所以我也不能改变这个数据类型。

我认为问题出在对的每个值上,我执行了Contains 操作,这会减慢 who 进程,但我找不到替代方法...

这是我为了找到匹配的代码所做的。
但是这段代码的性能很糟糕(完成这一行代码大约需要 5 分钟)。

有人可以帮忙吗?


更新和最简单的解决方案:

我终于找到了为什么搜索这么慢的季节,并通过这样做解决了:

var relevantStringIDs = this.dic.Where(ele => ele.Value.Contains(keyword)).Tolist();
var relevantUlongIDs = relevantStringIDs.Select(n => Convert.ToUInt64(n.Key)).Tolist();

这么慢的原因是this.dic.Where(ele => ele.Value.Contains(keyword)),每次执行第二部分查询时都会执行(这是IEnumberable<T>的特点,我忘记了它的术语(可能延迟执行))。所以我使用ToList()将延迟查询转换为内存中的具体列表,以便在将字符串转换为ulongs时可以重复使用结果,而不是每次转换都再次执行查询。
如果您在此说明中发现错误,请纠正我。

顺便说一句,虽然这可能不是最好的解决方案,但更改后的代码的性能还是令人满意的。代码的第一条语句只花费了 169 毫秒,这对我来说已经足够快了。

【问题讨论】:

  • 大家好,请不要在不告诉我原因的情况下否决我的问题。我检查了其他类似的问题,但没有一个真正解决了我的问题。
  • 如果您的代码可以包含字符,那么您将很难将它们全部转换为 Int64...
  • 术语是“deferred execution”。但是,在WhereSelect 之间发出额外的ToList 在这种情况下不太可能有助于提高性能。如果您多次迭代最终结果(我假设您是这样),那么您主要受益于 final ToList。尝试将您的代码从 dic.Where(...).ToList().Select(...).ToList() 更改为 dic.Where(...).Select(...).ToList() - 您将观察到性能几乎没有变化。

标签: c# performance algorithm search dictionary


【解决方案1】:

您的问题是,通过迭代字典的值,您失去了字典的所有速度优势。字典针对键查找进行了优化。

我会使用不同的数据类型来处理这个问题,并针对您的关键字查找进行优化。

这是一个使用 LINQ 从与您的数据相似的数据创建 Lookup 的示例。在这种情况下,我直接从字符串数据构建它,这完全避免了字典。

这种类型的查找应该会执行得更好。

string [] lines = {
"123 A, B, C, D",
"456 E, F, G",
"321 A, E, H, I",
"654 B, G",
"789 A, J, K, L",
"987 A, M, L, E"
};

var lookup = lines.SelectMany (
    l => (l.Split(new char[]{' '},2)[1]).Split(',').Select (v => v.Trim().ToLower()).ToArray(),
    (l,o) => new{
    keyword = o,
    code = Convert.ToInt64(l.Split(' ')[0])
}).ToLookup(k => k.keyword, v => v.code).Dump();

Console.WriteLine(String.Join(",",lookup["a"]));
Console.WriteLine(String.Join(",",lookup["l"]));
Console.WriteLine(String.Join(",",lookup["b"]));

请注意,这假设您正在查找整个关键字(您的初始示例可能会查找部分关键字)

【讨论】:

  • 谢谢马克 :) 我现在试试
【解决方案2】:

你做错了。当您知道而不是值时,字典允许高效查找。

修复性能的一种简单方法是构建一个反向字典,模仿内容的全文索引:

var dic = new Dictionary<string, string>();
dic.Add("910235487", "Diabetes, tumors, sugar sick");
dic.Add("120391052", "Fever, diabetes");

char[] delimiters = new char[] { ' ', ',' };

var wordCodes =
    from kvp in dic
    from word in kvp.Value.Split(delimiters, StringSplitOptions.RemoveEmptyEntries)
    let code = long.Parse(kvp.Key)
    select new { Word = word, Code = code };

var fullTextIndex =
    wordCodes.ToLookup(wc => wc.Word, wc => wc.Code, StringComparer.OrdinalIgnoreCase);

long[] test1 = fullTextIndex["sugar"].ToArray();       // Gives 910235487
long[] test2 = fullTextIndex["diabetes"].ToArray();    // Gives 910235487, 120391052

全文索引的构建需要很长时间;但是,这是一次性成本,将通过后续查找所节省的时间进行摊销。

【讨论】:

  • 谢谢,我正在努力:)
  • 嗨,Douglas,我发现问题隐藏在我的代码中。但无论如何我喜欢你的方式,谢谢:)
猜你喜欢
  • 2013-05-07
  • 1970-01-01
  • 2017-11-11
  • 2014-09-19
  • 1970-01-01
  • 1970-01-01
  • 2014-01-14
  • 2013-10-27
  • 2023-01-05
相关资源
最近更新 更多