首先,我会更改
ListControl
如何查看您的数据源,您正在将结果
IEnumerable<string>
转换为
List<string>
。特别是当您只输入了几个字符时,这可能是低效的(也是不必要的)。
不要制作昂贵的数据副本。
- 我会将
.Where()
结果包装到一个仅实现所需内容(搜索)的集合中。这将使您避免为每个键入的字符创建一个新的大列表。
- 作为替代方案,我会避免使用LINQ,并编写更具体(和优化的)代码。将列表保留在内存中,并构建匹配索引的数组,重复使用数组,这样您就不必为每次搜索重新分配它。
其次,当小列表足够时,请勿在大列表中进行搜索。当用户开始输入“ab”并添加“c”时,您不需要在大列表中重新搜索,在过滤列表中搜索就足够了(而且更快)。
尽可能完善搜索,不要每次执行完整搜索。
第三步可能更难: 保持数据有序以便快速搜索。现在,您需要更改用于存储数据的结构。想象一棵像这样的树:
A B 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)来避免回溯。它们很昂贵,通常使用幂集构造来创建,但使用起来非常快。
- 存根:Knuth-Morris-Pratt计算识别以要搜索的字符串作为后缀的输入的DFA,Boyer-Moore从针尖开始搜索,因此每次可以跳过整个针长。Baeza-Yates跟踪之前的j个字符是否是搜索字符串的前缀,因此适用于模糊字符串搜索。Bitap算法是Baeza-Yates方法的一种应用。
- 索引方法:更快的搜索算法基于预处理文本。例如,在构建子字符串索引(例如后缀树或后缀数组)之后,可以快速找到模式的出现。
- 其他变体:有些搜索方法(如trigram搜索)旨在查找搜索字符串与文本之间的“接近程度”得分,而不是“匹配/不匹配”。它们有时被称为“模糊”搜索。
关于并行搜索的几句话。虽然可能可行,但很少是简单的,因为使其并行的开销往往比搜索本身更高。我不会将搜索本身并行化(分区和同步很快就会变得过于昂贵,也许还很复杂),但我会将搜索移动到一个单独的线程中。如果主线程没有忙碌,用户在输入时就不会感到任何延迟(如果列表出现在200毫秒后,他们不会注意到,但如果他们必须等待50毫秒后才能继续输入,他们会感到不舒服)。当然,搜索本身必须足够快,在这种情况下,您不使用线程来加速搜索,而是使用线程来保持您的UI响应。请注意,单独的线程不会使您的查询更快,它不会挂起UI,但如果您的查询很慢,它仍然会很慢(而且您还必须处理多个连续请求)。
HashSet<T>
无法帮助你,因为你正在搜索字符串的 部分。 - Dennis