【发布时间】:2011-12-30 02:17:10
【问题描述】:
List<T> 中有大约 10,000 名员工的列表,ListBox 包含这些员工的子集,具体取决于文本框中的搜索词。
假设Staff 对象具有以下公开的属性:
string FirstName
string LastName
string MiddleName
int StaffID
int CostCentre
我可以写一个这样的函数:
bool staffMatchesSearch(Staff stf)
{
if (tbSrch.Text.Trim() == string.Empty)
return true; // No search = match always.
string s = tbSrch.Text.Trim().ToLower();
// Do the checks in the order most likely to return soonest:
if (stf.LastName.ToLower().Contains(s))
return true;
if (stf.FirstName.ToLower().Contains(s))
return true;
if (stf.MiddleName.ToLower().Contains(s))
return true;
if (stf.CostCentre.ToString().Contains(s))
return true; // Yes, we want partial matches on CostCentre
if (stf.StaffID.ToString().Contains(s))
return true; // And also on StaffID
return false;
}
然后执行以下操作:
tbSrch_TextChanged(object sender, EventArgs e)
{
lbStaff.BeginUpdate();
lbStaff.Items.Clear();
foreach (Staff stf in staff)
if (staffMatchesSearch(stf))
lbStaff.Items.Add(stf);
lbStaff.EndUpdate();
}
每次用户更改tbSrch 框的内容时,都会重新评估过滤。
这行得通,而且它不是非常慢,但我想知道我是否可以让它更快?
我曾尝试将整个内容重写为多线程,但是只有 10,000 名员工,开销似乎带走了大部分好处。此外,还有许多其他错误,例如如果搜索“John”,用户首先按下“J”,它会缠绕线程,但是当用户按下“o”时,另一组在第一批之前被缠绕有机会返回他们的结果。很多时候,结果以混乱的顺序返回,并且会发生各种令人讨厌的事情。
我可以想到一些调整,可以使最好的情况明显更好,但也会使最坏的情况变得更糟。
您对如何改进有任何想法吗?
我已经实施的好建议及其结果:
- 在
ValueChanged事件中添加延迟,这样如果用户在键盘上快速键入 5 个字符的名称,它只会在末尾执行 1 次搜索,而不是连续执行 5 次搜索。 - 预评估
ToLower()并存储在Staff类中(作为[NonSerialized]属性,因此它不会占用保存文件中的额外空间)。 - 在
Staff中添加一个get属性,它将所有搜索条件作为单个、长、小写的串联字符串返回。然后在上面运行一个Contains()。 (这个字符串存储在Staff对象中,所以它只被构造一次。)
到目前为止,这些已将搜索时间从大约 140 毫秒降低到大约 60 毫秒(尽管这些数字非常主观,具体取决于执行的实际搜索和返回的结果数量)。
【问题讨论】:
-
你真的想
toString那些ints吗?好像你想要一个匹配字符串方法和匹配 int 方法......我的意思是,如果我在中心 13,我不应该出现,因为有人搜索中心 1 或中心 3...... -
尝试实现 Boyer-Moore 系列字符串搜索算法之一?预处理搜索词或 Staff 对象并重用结果可以节省大量时间。
-
您真的要每次都搜索每个员工属性的所有匹配项吗?作为用户,我希望一次只搜索一两个已知字段。
-
@glowcoder 这正是他们正在寻找的行为。
-
你能等到输入两个字母后再搜索,还是只在输入星号时才搜索?