Думаю, у мене є відповідь! Так, це правда, що Contains () у списку (масиві) має значення O (n), але якщо масив є коротким, і ви використовуєте типи значень, він все одно повинен бути досить швидким. Але, використовуючи CLR Profiler [завантажити безкоштовно від Microsoft], я виявив, що Contains () - це боксерські значення для їх порівняння, що вимагає розподілу купи, що ДУЖЕ дорого (повільно). [Примітка: Це .Net 2.0; інші версії .Net не перевірені.]
Ось повна історія та рішення. У нас є перелік, що називається "VI", і ми створили клас "ValueIdList", який є абстрактним типом для списку (масиву) об'єктів VI. Оригінальна реалізація була в старовинному .Net 1.1 днів і використовувала інкапсульований ArrayList. Нещодавно ми виявили в http://blogs.msdn.com/b/joshwil/archive/2004/04/13/112598.aspx, що загальний список (List <VI>) працює набагато краще, ніж ArrayList, для типів значень (наприклад, наш enum VI), оскільки значення не потрібно вставляти в рамки. Це правда, і це спрацювало ... майже.
CLR Profiler відкрив сюрприз. Ось частина Графіку розподілу:
- ValueIdList :: Містить bool (VI) 5,5 МБ (34,81%)
- Generic.List :: Містить bool (<UNKNOWN>) 5,5 МБ (34,81%)
- Generic.ObjectEqualityComparer <T> :: Дорівнює bool (<UNKNOWN> <UNKNOWN>) 5,5 МБ (34,88%)
- Цінності. VІ 7,7 МБ (49,03%)
Як бачите, Contains () напрочуд викликає Generic.ObjectEqualityComparer.Equals (), що, мабуть, вимагає боксу значення VI, що вимагає дорогого розподілу купи. Дивно, що Microsoft виключить бокс у списку, лише вимагаючи його знову для такої простої операції, як ця.
Нашим рішенням було переписати реалізацію Contains (), що в нашому випадку було легко зробити, оскільки ми вже інкапсулювали загальний об’єкт списку (_items). Ось простий код:
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;
}
Зараз порівняння значень VI робиться у нашій власній версії IndexOf (), яка не вимагає боксу, і це дуже швидко. Наша програма пришвидшилась на 20% після цього простого перезапису. O (n) ... немає проблем! Просто уникайте марного використання пам’яті!