У меня есть список из 10 000 сотрудников в 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 сотрудников накладные расходы, казалось, отняли большую часть выгоды. Кроме того, было множество других ошибок, например, при поиске "Джона", пользователь сначала нажимает "J", который разматывает потоки, но когда пользователь нажимает "o", другой набор наматывается до того, как первая партия шанс вернуть свои результаты. Много времени результаты возвращаются в беспорядочном порядке и происходят всевозможные неприятные вещи.
Я могу придумать несколько настроек, которые сделают лучший вариант сценария значительно лучше, но они также сделают худший сценарий намного хуже.
Есть ли у вас идеи о том, как это можно улучшить?
Отличные предложения, которые я реализовал далеко, и их результаты:
- Добавьте задержку в событие
ValueChanged
, чтобы, если пользователь быстро набирает 5-значное имя на клавиатуре, он выполняет только 1 поиск в конце, а не 5 в последовательности. - Предварительно оцените
ToLower()
и сохраните в классеStaff
(как атрибут[NonSerialized]
, чтобы он не занимал лишнего места в файле сохранения). - Добавить свойство
get
вStaff
, которое возвращает все критерии поиска как одну, длинную, строчную, конкатенированную строку. Затем запустите одинContains()
. (Эта строка хранится в объектеStaff
, поэтому она создается только один раз.)
До сих пор они сокращали время поиска с примерно 140 мс до примерно 60 мс (хотя эти цифры очень субъективны в зависимости от фактического выполнения поиска и количества возвращенных результатов).