Najszybszy sposób wyszukiwania w kolekcji ciągów

Problem:

Mam plik tekstowy około 120,000 użytkownicy (ciągi), które chciałbym zapisać w kolekcji, a następnie przeprowadzić wyszukiwanie w tej kolekcji.

Metoda wyszukiwania pojawi się za każdym razem, gdy użytkownik zmieni tekst TextBox, a wynikiem powinny być ciągi znaków, które zawierać tekst w TextBox.

Nie muszę zmieniać listy, po prostu wyciągnij wyniki i umieść je w ListBox.

Co mam wypróbowane do tej pory:

Próbowałem z dwoma różnymi kolekcjami/kontenerami, które wyrzucam wpisy łańcuchowe z zewnętrznego pliku tekstowego (oczywiście raz):

  1. List<string> allUsers;
  2. HashSet<string> allUsers;

Z następującymLINQ zapytaniem:

allUsers.Where(item => item.Contains(textBox_search.Text)).ToList();

Moje Zdarzenie wyszukiwania (wywołane po zmianie tekstu wyszukiwania przez Użytkownika):

private void textBox_search_TextChanged(object sender, EventArgs e)
{
    if (textBox_search.Text.Length > 2)
    {
        listBox_choices.DataSource = allUsers.Where(item => item.Contains(textBox_search.Text)).ToList();
    }
    else
    {
        listBox_choices.DataSource = null;
    }
}

Wyniki:

Oba dały mi słaby czas reakcji (około 1-3 sekund między każdym klawiszem prasa).

Pytanie:

Jak myślisz, gdzie jest moje wąskie gardło? Kolekcja, z której korzystałem? Metoda wyszukiwania? Jedno i drugie?

Jak mogę uzyskać lepszą wydajność i bardziej płynną funkcjonalność?

Author: Adriano Repetti, 2014-01-27

17 answers

Możesz rozważyć wykonanie zadania filtrowania w wątku tła, który wywoła metodę wywołania zwrotnego po zakończeniu, lub po prostu uruchom filtrowanie ponownie, jeśli wejście zostanie zmienione.

Ogólna idea polega na tym, aby móc używać go w ten sposób:

public partial class YourForm : Form
{
    private readonly BackgroundWordFilter _filter;

    public YourForm()
    {
        InitializeComponent();

        // setup the background worker to return no more than 10 items,
        // and to set ListBox.DataSource when results are ready

        _filter = new BackgroundWordFilter
        (
            items: GetDictionaryItems(),
            maxItemsToMatch: 10,
            callback: results => 
              this.Invoke(new Action(() => listBox_choices.DataSource = results))
        );
    }

    private void textBox_search_TextChanged(object sender, EventArgs e)
    {
        // this will update the background worker's "current entry"
        _filter.SetCurrentEntry(textBox_search.Text);
    }
}

Szorstki szkic byłby czymś w stylu:

public class BackgroundWordFilter : IDisposable
{
    private readonly List<string> _items;
    private readonly AutoResetEvent _signal = new AutoResetEvent(false);
    private readonly Thread _workerThread;
    private readonly int _maxItemsToMatch;
    private readonly Action<List<string>> _callback;

    private volatile bool _shouldRun = true;
    private volatile string _currentEntry = null;

    public BackgroundWordFilter(
        List<string> items,
        int maxItemsToMatch,
        Action<List<string>> callback)
    {
        _items = items;
        _callback = callback;
        _maxItemsToMatch = maxItemsToMatch;

        // start the long-lived backgroud thread
        _workerThread = new Thread(WorkerLoop)
        {
            IsBackground = true,
            Priority = ThreadPriority.BelowNormal
        };

        _workerThread.Start();
    }

    public void SetCurrentEntry(string currentEntry)
    {
        // set the current entry and signal the worker thread
        _currentEntry = currentEntry;
        _signal.Set();
    }

    void WorkerLoop()
    {
        while (_shouldRun)
        {
            // wait here until there is a new entry
            _signal.WaitOne();
            if (!_shouldRun)
                return;

            var entry = _currentEntry;
            var results = new List<string>();

            // if there is nothing to process,
            // return an empty list
            if (string.IsNullOrEmpty(entry))
            {
                _callback(results);
                continue;
            }

            // do the search in a for-loop to 
            // allow early termination when current entry
            // is changed on a different thread
            foreach (var i in _items)
            {
                // if matched, add to the list of results
                if (i.Contains(entry))
                    results.Add(i);

                // check if the current entry was updated in the meantime,
                // or we found enough items
                if (entry != _currentEntry || results.Count >= _maxItemsToMatch)
                    break;
            }

            if (entry == _currentEntry)
                _callback(results);
        }
    }

    public void Dispose()
    {
        // we are using AutoResetEvent and a background thread
        // and therefore must dispose it explicitly
        Dispose(true);
    }

    private void Dispose(bool disposing)
    {
        if (!disposing)
            return;

        // shutdown the thread
        if (_workerThread.IsAlive)
        {
            _shouldRun = false;
            _currentEntry = null;
            _signal.Set();
            _workerThread.Join();
        }

        // if targetting .NET 3.5 or older, we have to
        // use the explicit IDisposable implementation
        (_signal as IDisposable).Dispose();
    }
}

Należy również usunąć instancję _filter, gdy rodzic Form zostanie usunięty. Oznacza to, że należy otworzyć i edytować metodę Form Dispose (wewnątrz pliku YourForm.Designer.cs), aby wyszukać coś w stylu:

// inside "xxxxxx.Designer.cs"
protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        if (_filter != null)
            _filter.Dispose();

        // this part is added by Visual Studio designer
        if (components != null)
            components.Dispose();
    }

    base.Dispose(disposing);
}

Na mojej maszynie działa dość szybko, więc powinieneś przetestować i profilować to przed pójściem na bardziej złożone rozwiązanie.

To powiedziawszy, "bardziej złożonym rozwiązaniem" byłoby prawdopodobnie przechowywanie ostatnich kilku wyników w słowniku, a następnie filtrowanie ich tylko wtedy, gdy okaże się, że nowy wpis różni się tylko pierwszym z ostatnich znaków.

 49
Author: Groo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-29 10:13:00

Przeprowadziłem kilka testów i przeszukanie listy 120 000 pozycji i wypełnienie nowej listy wpisami zajmuje znikomą ilość czasu(około 1/50 sekundy, nawet jeśli wszystkie ciągi są dopasowane).

Problem, który widzisz, musi wynikać z wypełnienia źródła danych, tutaj:

listBox_choices.DataSource = ...

Podejrzewam, że po prostu wkładasz zbyt wiele przedmiotów do listboxa.

Może powinieneś spróbować ograniczyć go do pierwszych 20 wpisów, jak więc:

listBox_choices.DataSource = allUsers.Where(item => item.Contains(textBox_search.Text))
    .Take(20).ToList();

Zauważ również (jak zauważyli inni), że uzyskujesz dostęp do właściwości TextBox.Text dla każdego elementu w allUsers. Można to łatwo naprawić w następujący sposób:

string target = textBox_search.Text;
listBox_choices.DataSource = allUsers.Where(item => item.Contains(target))
    .Take(20).ToList();

Jednak zmierzyłem czas, jak długo trwa dostęp TextBox.Text 500 000 razy i zajęło to tylko 0,7 sekundy, znacznie mniej niż 1-3 sekundy wymienione w OP. mimo to, jest to warta optymalizacji.

 37
Author: Matthew Watson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-28 09:16:39

Użyj drzewa przyrostków jako indeksu. A raczej po prostu zbudować posortowany słownik, który kojarzy każdy sufiks każdej nazwy z listą odpowiadających im nazw.

Dla wejścia:

Abraham
Barbara
Abram

Struktura wyglądałaby następująco:

a -> Barbara
ab -> Abram
abraham -> Abraham
abram -> Abram
am -> Abraham, Abram
aham -> Abraham
ara -> Barbara
arbara -> Barbara
bara -> Barbara
barbara -> Barbara
bram -> Abram
braham -> Abraham
ham -> Abraham
m -> Abraham, Abram
raham -> Abraham
ram -> Abram
rbara -> Barbara

Algorytm wyszukiwania

Załóżmy wejście użytkownika "bra".

  1. Bisect słownik na wejściu użytkownika, aby znaleźć wejście użytkownika lub pozycję, w której może się udać. W ten sposób znajdujemy "barbara" - ostatni klucz niższy niż "bra". Jest to tzw. dolna granica "bra". Wyszukiwanie zajmie czas logarytmiczny.
  2. powtarzaj od znalezionego klucza, aż Dane wejściowe użytkownika nie będą już dopasowane. Dałoby to "bram" - > Abram i" braham " - > Abraham.
  3. Połącz wynik iteracji (Abram, Abraham) i wyprowadź go.

Takie drzewa są przeznaczone do szybkiego wyszukiwania podciągów. Jego wydajność jest bliska O (log n). Wierzę, że to podejście będzie działać wystarczająco szybko, aby być używane przez GUI wątku bezpośrednio. Ponadto będzie działać szybciej niż rozwiązanie gwintowane ze względu na brak synchronizacji narzutów.

 29
Author: Basilevs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:31:59

Potrzebujesz albo wyszukiwarki tekstowej (jak Lucene.Net ), lub bazy danych (można rozważyć wbudowany jak SQL CE, SQLite , itd.). Innymi słowy, potrzebujesz zindeksowanego wyszukiwania. Wyszukiwanie oparte na haszu nie ma tutaj zastosowania, ponieważ szukasz podciągu, podczas gdy wyszukiwanie oparte na haszu jest dobre do wyszukiwania dokładnej wartości.

W przeciwnym razie będzie to iteracyjne wyszukiwanie z zapętleniem kolekcji.

 15
Author: Dennis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-27 15:51:30

Przydatne może być również zdarzenie typu "debounce". Różni się to od dławienia tym, że czeka pewien okres czasu (na przykład 200 ms) na zakończenie zmian przed uruchomieniem zdarzenia.

Zobacz Debounce and Throttle: a visual explanation Więcej informacji na temat debouncingu. Doceniam, że ten artykuł jest JavaScript koncentruje się, zamiast C#, ale zasada ma zastosowanie.

Zaletą tego jest to, że nie wyszukuje, gdy wciąż wpisujesz Twoje zapytanie. Następnie powinien przestać próbować wykonywać dwa wyszukiwania naraz.

 13
Author: paulslater19,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-27 15:48:21

Uruchom wyszukiwanie w innym wątku i pokaż animację ładowania lub pasek postępu, gdy ten wątek jest uruchomiony.

Możesz również spróbować równoległe zapytanieLINQ .

var queryResults = strings.AsParallel().Where(item => item.Contains("1")).ToList();

Oto benchmark, który demonstruje zalety działania funkcji AsParallel ():

{
    IEnumerable<string> queryResults;
    bool useParallel = true;

    var strings = new List<string>();

    for (int i = 0; i < 2500000; i++)
        strings.Add(i.ToString());

    var stp = new Stopwatch();

    stp.Start();

    if (useParallel)
        queryResults = strings.AsParallel().Where(item => item.Contains("1")).ToList();
    else
        queryResults = strings.Where(item => item.Contains("1")).ToList();

    stp.Stop();

    Console.WriteLine("useParallel: {0}\r\nTime Elapsed: {1}", useParallel, stp.ElapsedMilliseconds);
}
 12
Author: animaonline,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-27 15:46:10

Aktualizacja:

Zrobiłem profilowanie.

(Aktualizacja 3)

  • Zawartość listy: liczby wygenerowane od 0 do 2.499.999
  • Filtr tekstowy: 123 (20.477 wyniki)
  • Core i5-2500, Win7 64bit, 8GB RAM
  • VS2012 + JetBrains dotTrace
Pierwsze próby dla 2.500.000 rekordów zajęły mi 20.000 ms.]}

Numer jeden to wezwanie do textBox_search.Text wewnątrz Contains. To sprawia, że wywołanie dla każdego elementu do drogiego get_WindowText metoda pola tekstowego. Wystarczy zmienić kod na:

    var text = textBox_search.Text;
    listBox_choices.DataSource = allUsers.Where(item => item.Contains(text)).ToList();

Skrócono czas wykonania do 1.858 ms.

Update 2:

Pozostałe dwa znaczące Bottle-necks to teraz wywołanie string.Contains (około 45% czasu wykonania) i aktualizacja elementów listbox w set_Datasource (30%).

Moglibyśmy dokonać kompromisu między szybkością a zużyciem pamięci, tworząc drzewo sufiksów, jak zasugerował Basilevs, aby zmniejszyć liczbę niezbędnych porównań i wcisnąć kilka czas przetwarzania od wyszukania po naciśnięciu klawisza do załadowania nazw z pliku, które mogą być preferowane przez użytkownika.

Aby zwiększyć wydajność wczytywania elementów do listboxa proponuję załadować tylko kilka pierwszych elementów i wskazać użytkownikowi, że są jeszcze dostępne elementy. W ten sposób możesz przekazać użytkownikowi informację zwrotną, że są dostępne wyniki, aby mógł udoskonalić swoje wyszukiwanie, wpisując więcej liter lub załadować pełną listę za pomocą naciśnięcie przycisku.

Użycie BeginUpdate i EndUpdate nie zmieniło czasu wykonania set_Datasource.

jak zauważyli tutaj inni, samo zapytanie LINQ działa dość szybko. Wierzę, że twoja szyjka butelki jest aktualizacją samego listboxa. Możesz spróbować czegoś w stylu:

if (textBox_search.Text.Length > 2)
{
    listBox_choices.BeginUpdate(); 
    listBox_choices.DataSource = allUsers.Where(item => item.Contains(textBox_search.Text)).ToList();
    listBox_choices.EndUpdate(); 
}
Mam nadzieję, że to pomoże.
 12
Author: Andris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-30 12:04:09

Zakładając, że dopasowujesz tylko przedrostki, struktura danych, której szukasz, nazywa się trie , znanym również jako "drzewo prefiksów". Metoda IEnumerable.Where, której teraz używasz, będzie musiała sprawdzać wszystkie pozycje w twoim słowniku przy każdym dostępie.

Ten wątek pokazuje jak utworzyć trie w C#.

 10
Author: Groo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:15

Kontrolka WinForms ListBox jest tutaj twoim wrogiem. Ładowanie rekordów będzie powolne, a pasek przewijania będzie walczył z Tobą, aby pokazać wszystkie rekordy 120,000.

Spróbuj użyć staromodnego DataGridView danych-pozyskiwanych do DataTable z pojedynczą kolumną [nazwa użytkownika] do przechowywania danych:

private DataTable dt;

public Form1() {
  InitializeComponent();

  dt = new DataTable();
  dt.Columns.Add("UserName");
  for (int i = 0; i < 120000; ++i){
    DataRow dr = dt.NewRow();
    dr[0] = "user" + i.ToString();
    dt.Rows.Add(dr);
  }
  dgv.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
  dgv.AllowUserToAddRows = false;
  dgv.AllowUserToDeleteRows = false;
  dgv.RowHeadersVisible = false;
  dgv.DataSource = dt;
}

Następnie użyj widoku DataView w zdarzeniu TextChanged Twojego TextBox, aby filtrować dane:

private void textBox1_TextChanged(object sender, EventArgs e) {
  DataView dv = new DataView(dt);
  dv.RowFilter = string.Format("[UserName] LIKE '%{0}%'", textBox1.Text);
  dgv.DataSource = dv;
}
 8
Author: LarsTech,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-27 21:26:47

Najpierw zmieniłbym sposób, w jaki ListControl widzi źródło danych, zamieniasz wynik IEnumerable<string> na List<string>. Zwłaszcza, gdy wpisałeś tylko kilka znaków, może to być nieefektywne (i niepotrzebne). nie rób ekspansywnych kopii swoich danych.

  • chciałbym zawinąć .Where() wynik do zbioru, który implementuje tylko to, co jest wymagane od IList (wyszukiwanie). To pozwoli Ci utworzyć nową dużą listę dla każdego znaku jest wpisany.
  • jako alternatywę unikałbym LINQ ' a i pisałbym coś bardziej konkretnego (i zoptymalizowanego). Zachowaj listę w pamięci i zbuduj tablicę dopasowanych indeksów, używaj tablicy ponownie, aby nie trzeba było jej przydzielać dla każdego wyszukiwania.

Drugim krokiem jest nie wyszukiwanie na dużej liście, gdy wystarczy mała. Gdy użytkownik zaczął wpisywać " ab "i dodał" c " to nie trzeba szukać w dużej liście, wyszukiwanie w filtrowanej liście wystarczy (i szybciej). udoskonal wyszukiwanie za każdym razem jest to możliwe, nie wykonuj pełnego wyszukiwania czas.

Trzeci krok może być trudniejszy: Uporządkuj dane w celu szybkiego przeszukiwania. Teraz musisz zmienić strukturę używaną do przechowywania danych. wyobraź sobie takie drzewo:

A        B         C
 Add      Better    Ceil
 Above    Bone      Contour

Może to być po prostu zaimplementowane za pomocą tablicy (jeśli pracujesz z nazwami ANSI, w przeciwnym razie lepszy byłby słownik). Zbuduj listę w ten sposób (w celach ilustracyjnych, pasuje do początku łańcucha):

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 wykonane najpierw za pomocą postać:

char letter = textBox_search.Text[0];
if (dictionary.Contains(letter))
{
    listBox_choices.DataSource =
        new MyListWrapper(dictionary[letter].Where(x => x.Contains(textBox_search.Text)));
}

Proszę zauważyć, ż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 dla klucza słownikowego, możesz zachować każdą listę krótko i szybko, aby - być może - uniknąć czegokolwiek innego). Ponadto należy pamiętać, że można spróbować użyć dwóch pierwszych znaków w słowniku(więcej list i krótsze). Jeśli rozszerzysz to będziesz miał drzewo (ale nie sądzę, że masz tak dużą liczbę elementów).

Istnieje wiele różnych algorytmy do wyszukiwania ciągów (z powiązanymi strukturami danych), by wymienić tylko kilka:

  • wyszukiwanie oparte na automacie skończonym (finite state automaton based search) : w tym podejściu unikamy cofania poprzez konstruowanie deterministycznego automatu skończonego (Dfa), który rozpoznaje przechowywany ciąg wyszukiwania. Są one kosztowne w budowie-są zwykle tworzone przy użyciu konstrukcji powerset-ale są bardzo szybkie w użyciu.
  • Stubs : Knuth-Morris-Pratt oblicza DFA, który rozpoznaje wejścia z ciągiem do wyszukiwania jako przyrostek, Boyer-Moore rozpoczyna wyszukiwanie od końca igły, więc zwykle może przeskoczyć całą długość igły na każdym kroku. Baeza-Yates śledzi, czy poprzednie znaki j były przedrostkiem szukanego ciągu i dlatego można je dostosować do wyszukiwania rozmytego ciągu. Algorytm bitap jest zastosowaniem podejścia Baeza-Yatesa.
  • metody indeksowe : algorytmy szybszego wyszukiwania są oparte na wstępnej obróbce tekstu. Po budując indeks podłańcucha, na przykład drzewo sufiksów lub tablicę 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ą one czasami nazywane "rozmyte" wyszukiwania.

Kilka słów o wyszukiwaniu równoległym. Jest to możliwe, ale rzadko jest to banalne, ponieważ może być łatwo znacznie wyższy niż samo wyszukiwanie. Nie wykonywałbym wyszukiwania równolegle (partycjonowanie i synchronizacja wkrótce staną się zbyt ekspansywne i może złożone), ale przeniosłbym wyszukiwanie do osobnego wątku . Jeśli główny wątek nie jest zajęty , użytkownicy nie odczują opóźnienia podczas pisania (nie zauważą, czy lista pojawi się po 200 ms, ale poczują się nieswojo, jeśli będą musieli poczekać 50 ms po wpisaniu). Oczywiście samo wyszukiwanie musi być wystarczająco szybkie, w w tym przypadku nie używasz wątków, aby przyspieszyć wyszukiwanie, ale aby zachować responsywność interfejsu. Należy pamiętać, że oddzielny wątek nie sprawi, że Twoje zapytanie będzie szybsze , nie zawiesi interfejsu użytkownika, ale jeśli Twoje zapytanie było powolne, nadal będzie powolne w oddzielnym wątku(ponadto musisz obsługiwać wiele kolejnych żądań).

 8
Author: Adriano Repetti,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-28 11:30:59

Możesz spróbować użyć PLINQ (Parallel LINQ). Chociaż nie gwarantuje to zwiększenia prędkości, to musisz dowiedzieć się metodą prób i błędów.

 5
Author: D. Gierveld,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-27 11:18:34

Wątpię, że będziesz w stanie zrobić to szybciej, ale na pewno powinieneś: {]}

A) użyj metody rozszerzenia AsParallel LINQ

A) użyj pewnego rodzaju timera, aby opóźnić filtrowanie

B) umieść metodę filtrowania na innym wątku

Trzymaj gdzieś coś string previousTextBoxValue. Zrób timer z opóźnieniem 1000 ms, które uruchamia wyszukiwanie na tick, Jeśli previousTextBoxValue jest taka sama jak wartość textbox.Text. Jeśli nie - Przypisz previousTextBoxValue do bieżącej wartości i zresetuj timer. Ustaw timer start na textbox zmienił Zdarzenie, a to sprawi, że aplikacja będzie płynniejsza. Filtrowanie 120 000 rekordów w ciągu 1-3 sekund jest OK, ale interfejs użytkownika musi pozostać responsywny.

 5
Author: Tarec,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-28 08:28:26
 4
Author: NeverHopeless,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-28 07:54:01

Starałbym się sortować kolekcję, wyszukiwać, aby pasowała tylko część startowa i ograniczać wyszukiwanie według jakiejś liczby.

So on ininialization

allUsers.Sort();

I Szukaj

allUsers.Where(item => item.StartWith(textBox_search.Text))

Może możesz dodać trochę pamięci podręcznej.

 3
Author: hardsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-27 16:03:10

Użyj Parallel LINQ. PLINQ jest równoległą implementacją LINQ do obiektów. PLINQ implementuje pełny zestaw standardowych operatorów zapytań LINQ jako metody rozszerzenia dla systemu T:.Przestrzeń nazw Linq i posiada dodatkowe operatory dla operacji równoległych. PLINQ łączy prostotę i czytelność składni LINQ z siłą programowania równoległego. Podobnie jak kod skierowany do biblioteki równoległej zadania, zapytania PLINQ skalują się w stopniu współbieżności w oparciu o możliwości komputera-hosta.

Wprowadzenie do PLINQ

Zrozumienie Speedup w PLINQ

Możesz również użyć Lucene.Net

Lucene.Net jest portem Lucene search engine library, napisanym w C# i skierowane do użytkowników. NET runtime. Biblioteka wyszukiwania Lucene jest na podstawie odwróconego indeksu. Lucene.Net ma trzy podstawowe cele:

 1
Author: Shahrooz Jafari,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-27 16:56:18

Zgodnie z tym co widziałem zgadzam się z faktem posortowania listy.

Jednak sortowanie kiedy lista jest konstruowana będzie bardzo powolne, sortowanie podczas budowania, będziesz miał lepszy czas wykonania.

W przeciwnym razie, jeśli nie musisz wyświetlać listy Lub zachować kolejność, użyj hashmapy.

Hashmap będzie hashować Twój ciąg i szukać w dokładnym offsecie. Myślę, że powinno być szybciej.

 1
Author: dada,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-28 04:14:31

Spróbuj użyć metody BinarySearch powinna działać szybciej niż zawiera metodę.

Zawiera będzie O (n) BinarySearch jest O(lg (n))

Myślę, że sortowana kolekcja powinna działać szybciej przy wyszukiwaniu i wolniej przy dodawaniu nowych elementów, ale jak rozumiem masz tylko problem z wyszukiwaniem.

 1
Author: user2917540,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-29 08:44:16