首先,我将更改如何ListControl
查看数据源,将结果转换IEnumerable<string>
为List<string>
。尤其是当您仅键入几个字符时,这可能是低效的(并且是不必要的)。不要对数据进行扩展复制。
- 我将
.Where()
结果包装到仅实现IList
(搜索)所需内容的集合中。这将节省您为键入的每个字符创建一个新的大列表。
- 作为替代方案,我会避免使用LINQ,而我会写一些更具体(和优化)的东西。将列表保存在内存中,并构建一个匹配索引的数组,然后重用该数组,这样您就不必为每次搜索重新分配它。
第二步是在小列表就足够时不要在大列表中搜索。当用户开始键入“ ab”并添加“ c”时,您无需在大列表中进行研究,在过滤后的列表中搜索就足够了(并且速度更快)。可以每次都进行细化搜索,而不是每次都不执行完整搜索。
第三步可能更困难:保持数据的组织以便快速搜索。现在,您必须更改用于存储数据的结构。想象这样的一棵树:
美国广播公司
添加更好的细胞
骨轮廓上方
这可以简单地用数组来实现(如果您使用的是ANSI名称,则最好使用字典)。像这样构建列表(出于说明目的,它匹配字符串的开头):
var dictionary = new Dictionary<char, List<string>>();
foreach (var user in users)
{
char letter = user[0];
if (dictionary.Contains(letter))
dictionary[letter].Add(user);
else
{
var newList = new List<string>();
newList.Add(user);
dictionary.Add(letter, newList);
}
}
然后将使用第一个字符进行搜索:
char letter = textBox_search.Text[0];
if (dictionary.Contains(letter))
{
listBox_choices.DataSource =
new MyListWrapper(dictionary[letter].Where(x => x.Contains(textBox_search.Text)));
}
请注意,我MyListWrapper()
按照第一步的建议使用(但为了简洁起见,我省略了第二条建议,如果您为字典键选择合适的大小,则可以使每个列表简短而快速-也许-避免其他事情)。此外,请注意,您可能会尝试在字典中使用前两个字符(更多列表,更短列表)。如果您对此进行扩展,则会有一棵树(但我认为您没有那么多的项目)。
字符串搜索有很多不同的算法(具有相关的数据结构),仅举几例:
- 基于有限状态自动机的搜索:在这种方法中,我们通过构造可识别存储的搜索字符串的确定性有限自动机(DFA)来避免回溯。这些构建起来很昂贵-它们通常是使用powerset构造创建的-但使用起来非常快。
- 存根:Knuth–Morris–Pratt计算出一个DFA,该DFA识别要搜索的带有字符串的输入作为后缀,Boyer–Moore从针的末端开始搜索,因此它通常可以在每一步向前跳过整个针的长度。Baeza-Yates会跟踪先前的j个字符是否是搜索字符串的前缀,因此适用于模糊字符串搜索。bitap算法是Baeza–Yates的方法的一种应用。
- 索引方法:更快的搜索算法基于文本的预处理。建立子字符串索引(例如后缀树或后缀数组)后,可以快速找到模式的出现。
- 其他变体:某些搜索方法(例如,三字母组搜索)旨在在搜索字符串和文本之间找到“接近度”得分,而不是“匹配/不匹配”。这些有时称为“模糊”搜索。
关于并行搜索的几句话。有可能,但很少琐碎,因为使其并行的开销可能很容易超出搜索本身。我不会并行执行搜索(分区和同步很快会变得过于扩展,甚至可能变得很复杂),但我会将搜索移到单独的线程中。如果主线程不忙,则用户在键入时不会感到任何延迟(他们不会注意到列表将在200 ms之后出现,但是如果键入后必须等待50 ms,他们会感到不舒服) 。当然,搜索本身必须足够快,在这种情况下,您无需使用线程来加快搜索速度,而是保持UI响应速度。请注意,单独的线程不会进行查询更快,它不会挂起UI,但是如果查询很慢,则在单独的线程中它仍然很慢(此外,您还必须处理多个顺序请求)。
HashSet<T>
在这里对您无济于事,因为您正在搜索字符串的一部分。