Myślę, że mam odpowiedź! Tak, to prawda, że Contains () na liście (tablicy) to O (n), ale jeśli tablica jest krótka i używasz typów wartości, nadal powinna być dość szybka. Ale używając CLR Profiler [darmowe pobranie z Microsoft], odkryłem, że Contains () pakuje wartości w celu ich porównania, co wymaga alokacji sterty, co jest BARDZO kosztowne (powolne). [Uwaga: to jest .Net 2.0; inne wersje .Net nie zostały przetestowane.]
Oto pełna historia i rozwiązanie. Mamy wyliczenie o nazwie "VI" i stworzyliśmy klasę o nazwie "ValueIdList", która jest typem abstrakcyjnym dla listy (tablicy) obiektów VI. Oryginalna implementacja była w starożytnym .Net 1.1 dnia i korzystała z hermetyzowanej listy ArrayList. Niedawno odkryliśmy na http://blogs.msdn.com/b/joshwil/archive/2004/04/13/112598.aspx, że lista ogólna (List <VI>) działa znacznie lepiej niż ArrayList w przypadku typów wartości (takich jak nasza enum VI), ponieważ wartości nie muszą być opakowane. To prawda i zadziałało ... prawie.
CLR Profiler ujawnił niespodziankę. Oto część wykresu alokacji:
- ValueIdList :: Zawiera bool (VI) 5,5 MB (34,81%)
- Generic.List :: Zawiera bool (<UNKNOWN>) 5,5 MB (34,81%)
- Generic.ObjectEqualityComparer <T> :: Equals bool (<UNKNOWN> <UNKNOWN>) 5,5 MB (34,88%)
- Wartości VI 7,7 MB (49,03%)
Jak widać, Contains () w zaskakujący sposób wywołuje Generic.ObjectEqualityComparer.Equals (), co najwyraźniej wymaga pakowania wartości VI, co wymaga kosztownego przydzielenia sterty. To dziwne, że Microsoft wyeliminowałby boks na liście, tylko po to, aby ponownie wymagać go do prostej operacji, takiej jak ta.
Naszym rozwiązaniem było ponowne napisanie implementacji Contains (), co w naszym przypadku było łatwe, ponieważ już hermetyzowaliśmy ogólny obiekt listy (_items). Oto prosty kod:
public bool Contains(VI id)
{
return IndexOf(id) >= 0;
}
public int IndexOf(VI id)
{
int i, count;
count = _items.Count;
for (i = 0; i < count; i++)
if (_items[i] == id)
return i;
return -1;
}
public bool Remove(VI id)
{
int i;
i = IndexOf(id);
if (i < 0)
return false;
_items.RemoveAt(i);
return true;
}
Porównanie wartości VI jest teraz wykonywane w naszej własnej wersji IndexOf (), która nie wymaga boksu i jest bardzo szybka. Nasz konkretny program przyspieszył o 20% po tym prostym przepisaniu. O (n) ... nie ma problemu! Po prostu unikaj marnowania pamięci!