По- перше я хотів би змінити , як ListControl
бачить джерело даних, ви перетворюєте результат IEnumerable<string>
в List<string>
. Особливо, коли ви просто набрали кілька символів, це може бути неефективним (і непотрібним). Не робіть обширних копій ваших даних .
- Я б переніс
.Where()
результат у колекцію, яка реалізує лише те, що вимагається від IList
(пошук). Це допоможе вам створити новий великий список для кожного введеного символу.
- В якості альтернативи я б уникав LINQ і писав би щось більш конкретне (та оптимізоване). Зберігайте свій список у пам'яті та створюйте масив відповідних індексів, повторно використовуйте масив, щоб вам не довелося перерозподіляти його для кожного пошуку.
Другий крок - не шукати у великому списку, коли достатньо малого. Коли користувач почав набирати "ab", і він додає "c", тоді вам не потрібно досліджувати у великому списку, достатньо (і швидше) пошуку у відфільтрованому списку. Кожен раз уточнюйте пошук , не виконуйте кожен раз повний пошук.
Третій крок може бути складнішим: зберегти дані упорядкованими для швидкого пошуку . Тепер вам доведеться змінити структуру, яка використовується для зберігання ваших даних. уявіть собі таке дерево:
ABC
Додайте кращу стелю
Над кістковим контуром
Це може бути просто реалізовано за допомогою масиву (якщо ви працюєте з іменами 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), який розпізнає збережений рядок пошуку. Вони дорогі для побудови - зазвичай вони створюються за допомогою конструкції блоку живлення, - але дуже швидкі у використанні.
- Заглушки : Кнут – Морріс – Пратт обчислює DFA, який розпізнає вхідні дані за допомогою рядка для пошуку як суфікс, Бойер – Мур починає пошук з кінця голки, тому він може перестрибувати на цілу довжину голки на кожному кроці. Baeza – Yates відстежує, чи були попередні символи j префіксом пошукового рядка, і тому пристосований до нечіткого пошуку рядків. Алгоритм бітап є застосуванням підходу Беези – Йейтса.
- Методи індексування : алгоритми швидшого пошуку засновані на попередній обробці тексту. Після побудови індексу підрядків, наприклад дерева суфіксів або суфіксального масиву, входження шаблону можна швидко знайти.
- Інші варіанти : деякі методи пошуку, наприклад, пошук у триграмі, призначені для пошуку оцінки "близькості" між пошуковим рядком та текстом, а не "збіг / невідповідність". Це іноді називають "нечіткими" пошуками.
Кілька слів про паралельний пошук. Це можливо, але це рідко буває тривіальним, оскільки накладні витрати, щоб зробити це паралельним, можуть бути набагато вищими, ніж сам пошук. Я б не виконував сам пошук паралельно (розділення та синхронізація стануть незабаром занадто обширними і, можливо, складними), але я б перемістив пошук в окремий потік . Якщо основний потік не зайнятий, ваші користувачі не відчуватимуть затримки під час введення тексту (вони не помітять, чи з'явиться список через 200 мс, але їм буде незручно, якщо їм доведеться чекати 50 мс після введення) . Звичайно, сам пошук повинен бути досить швидким, в цьому випадку ви не використовуєте потоки для пришвидшення пошуку, а щоб ваш інтерфейс відповідав . Зверніть увагу, що окремий потік не буде робити ваш запитшвидше , він не зависне інтерфейс, але якщо ваш запит був повільним, він все одно буде повільним в окремому потоці (крім того, вам також доведеться обробляти кілька послідовних запитів).
HashSet<T>
тут вам не допоможе, тому що ви шукаєте частину рядка.