czwartek, 6 grudnia 2012

Any w listach

Czasami w kodzie widzę coś takiego:
 IEnumerable<int> numbers= new List<int> { 1,2,3,11,22,33};

 if (numbers.Count() > 0)
 { 
   //code... 
 }

Nic wielkiego jeżeli mamy listę do tyś. elementów, ale co się stanie, gdy lista składa się z dużo większej liczby elementów lub elementy są jako 'ciężkie obiekty'. Metoda Count() ma liniową złożoność obliczeniową. Poniższa tabela przedstawia ilość elementów i czas działania metody Count w ms. Oczywiście na innym komputerze czas działania metody będzie inny.

CountCount time
1000
1,0000
10,0000
100,0001
1,000,00013
10,000,000130
100,000,0001332
1,000,000,00013243


Zastanawiam, się dlaczego programiści nie używają metody Any(), która ma złożoność O(1). Metoda ta sprawdza czy jest przynajmniej jeden element w liście. A co jeżeli będziemy chcieli sprawdzić czy w liście jest przynajmniej n-elementów? Ja wpadłem na parę rozwiązań.

if (numbers.Count() >= indexOf)
{
//count
}

if (numbers
.Select((x, i) => new { x, i })
.Any(arg => arg.i >= indexOf))
{
//select&any
}


if (numbers
.Skip(indexOf)
.Any())
{
//skip&any
}

if (numbers
.ElementAtOrDefault(indexOf+ 1) != default(int))
 {
 //elementAt
}

if (numbers
.Take(hasIndexes)
.Count() == indexOf)
{
//take&count
}


Zmienna indexOf określa ile elementów chcielibyśmy, aby było w liście. Metoda ElementAtOrDefault mogła by spełniać warunek zadania tylko wtedy, gdyby lista nie zawierała domyślnej wartości elementu. Poniżej przedstawiam wyniki czasu działania dla tych sposobów. Czas jest liczony w ms.

IndexOfCountSelect&AnySkip&AnyElementAtTake&Count
1282652101
10277690000
100279160000
1,000286040000
10,000284230000
100,000286053112
1,000,0002854132121421
10,000,00028408321128137205
100,000,000285293222130213902065
1,000,000,0002858332342132681378920489

Wykres w skali liniowej i logarytmicznej:



Wniosek jest taki, że najlepiej przeskoczyć potrzebną liczbę elementów w liście i sprawdzić czy jest jakiś element (skip&any).

Gdyby lista byłaby IList<T> to można jeszcze zastosować indexOf lub właściwość Count:

if (numbers
.IndexOf(hasIndexes) != -1)
{

}

Jeżeli znasz inne sposoby to opisz je w komentarzu. Jestem ciekaw co można jeszcze zrobić.

Brak komentarzy:

Prześlij komentarz