У меня есть список из примерно 10000 сотрудников в List<T>
, и у меня есть 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]
, чтобы он не занимал дополнительное место в файле сохранения).
- Добавьте свойство
get
в Staff
, которое возвращает все критерии поиска в виде единой длинной строчной строчной буквы. Затем запустите один Contains()
на этом. (Эта строка хранится в объекте Staff
, поэтому создается только один раз.)
Пока что время поиска уменьшено с 140 мс до 60 мс (хотя эти числа очень субъективны в зависимости от фактического поиска и количества возвращаемых результатов).