Po pierwsze chciałbym zmienić sposób ListControl
widzi źródło danych, jesteś konwersji wynik IEnumerable<string>
do List<string>
. Może to być nieefektywne (i niepotrzebne), zwłaszcza gdy wpisałeś tylko kilka znaków. Nie rób obszernych kopii swoich danych .
- Zawinąłbym
.Where()
wynik do kolekcji, która implementuje tylko to, co jest wymagane od IList
(wyszukiwanie). Pozwoli to zaoszczędzić na tworzeniu nowej dużej listy dla każdego wpisanego znaku.
- Alternatywnie unikałbym LINQ i napisałbym coś bardziej szczegółowego (i zoptymalizowanego). Przechowuj listę w pamięci i buduj tablicę dopasowanych indeksów, wykorzystuj ponownie tablicę, aby nie trzeba było jej ponownie przydzielać przy każdym wyszukiwaniu.
Drugim krokiem jest nie przeszukiwanie dużej listy, gdy wystarczy mała. Gdy użytkownik zaczął wpisywać „ab” i dodawał „c”, nie ma potrzeby przeszukiwania dużej listy, wystarczy przeszukać filtrowaną listę (i to szybciej). Zawęź wyszukiwanie każdym razem, gdy jest to możliwe, nie wykonuj za każdym razem pełnego wyszukiwania.
Trzeci krok może być trudniejszy: uporządkuj dane, aby można je było szybko przeszukiwać . Teraz musisz zmienić strukturę, której używasz do przechowywania danych. wyobraź sobie takie drzewo:
ABC
Dodaj lepszy sufit
Powyżej konturu kości
Można to po prostu zaimplementować za pomocą tablicy (jeśli pracujesz z nazwami ANSI, w przeciwnym razie słownik byłby lepszy). Zbuduj listę w ten sposób (dla celów ilustracyjnych, pasuje do początku ciągu):
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);
}
}
Wyszukiwanie zostanie przeprowadzone przy użyciu pierwszego znaku:
char letter = textBox_search.Text[0];
if (dictionary.Contains(letter))
{
listBox_choices.DataSource =
new MyListWrapper(dictionary[letter].Where(x => x.Contains(textBox_search.Text)));
}
Zwróć uwagę, że użyłem MyListWrapper()
zgodnie z sugestią w pierwszym kroku (ale pominąłem drugą sugestię dla zwięzłości, jeśli wybierzesz odpowiedni rozmiar klucza słownika, możesz sprawić, że każda lista będzie krótka i szybka, aby - być może - uniknąć czegokolwiek innego). Ponadto pamiętaj, że możesz spróbować użyć pierwszych dwóch znaków ze swojego słownika (więcej list i krótszych). Jeśli to rozszerzysz, otrzymasz drzewo (ale nie sądzę, że masz tak dużą liczbę przedmiotów).
Istnieje wiele różnych algorytmów wyszukiwania ciągów (z powiązanymi strukturami danych), aby wymienić tylko kilka:
- Wyszukiwanie oparte na automatach skończonych : w tym podejściu unikamy cofania się poprzez konstruowanie deterministycznego automatu skończonego (DFA), który rozpoznaje przechowywany ciąg wyszukiwania. Są drogie w budowie - zwykle są tworzone przy użyciu konstrukcji zestawu napędowego - ale są bardzo szybkie w użyciu.
- Stubs : Knuth – Morris – Pratt oblicza DFA, który rozpoznaje wejścia ze ciągiem do wyszukania jako przyrostek, Boyer – Moore rozpoczyna wyszukiwanie od końca igły, więc zwykle może przeskoczyć całą długość igły w każdym kroku. Baeza – Yates śledzi, czy poprzednie znaki j były prefiksem szukanego ciągu i dlatego można go dostosować do wyszukiwania rozmytego. Algorytm bitapowy jest zastosowaniem podejścia Baeza-Yatesa.
- Metody indeksowania : szybsze algorytmy wyszukiwania opierają się na wstępnym przetwarzaniu tekstu. Po zbudowaniu indeksu podciągów, na przykład drzewa sufiksów lub tablicy sufiksów, wystąpienia wzorca można szybko znaleźć.
- Inne warianty : niektóre metody wyszukiwania, na przykład wyszukiwanie trygramowe, mają na celu znalezienie wyniku „bliskości” między szukanym ciągiem a tekstem, a nie „dopasowania / braku dopasowania”. Są to czasami nazywane wyszukiwaniami „rozmytymi”.
Kilka słów o wyszukiwaniu równoległym. Jest to możliwe, ale rzadko jest to trywialne, ponieważ narzut, aby był równoległy, może być znacznie wyższy niż samo wyszukiwanie. Nie przeprowadzałbym samego wyszukiwania równolegle (partycjonowanie i synchronizacja wkrótce staną się zbyt rozbudowane i być może skomplikowane), ale przeniósłbym wyszukiwanie do osobnego wątku . Jeśli główny wątek nie jest zajęty, Twoi użytkownicy nie odczują żadnego opóźnienia podczas pisania (nie zauważą, czy lista pojawi się po 200 ms, ale poczują się nieswojo, jeśli będą musieli czekać 50 ms po wpisaniu) . Oczywiście samo wyszukiwanie musi być wystarczająco szybkie, w tym przypadku nie używasz wątków do przyspieszania wyszukiwania, ale do utrzymywania responsywności interfejsu użytkownika . Należy pamiętać, że osobny wątek nie spowoduje wysłania zapytaniaszybciej , nie zawiesi interfejsu użytkownika, ale jeśli twoje zapytanie było powolne, nadal będzie wolne w oddzielnym wątku (ponadto musisz obsługiwać również wiele kolejnych żądań).
HashSet<T>
nie pomoże ci tutaj, ponieważ szukasz części łańcucha.