Думаю, у меня есть ответ! Да, верно, что Contains () в списке (массиве) имеет значение O (n), но если массив короткий и вы используете типы значений, он все равно должен быть довольно быстрым. Но с помощью CLR Profiler [бесплатная загрузка от Microsoft] я обнаружил, что Contains () упаковывает значения для их сравнения, что требует выделения кучи, что ОЧЕНЬ дорого (медленно). [Примечание: это .Net 2.0; другие версии .Net не тестировались.]
Вот полная история и решение. У нас есть перечисление под названием «VI» и создан класс под названием «ValueIdList», который является абстрактным типом для списка (массива) объектов VI. Первоначальная реализация была в древнем .Net 1.1 days, и в ней использовался инкапсулированный 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> :: Equals bool (<UNKNOWN> <UNKNOWN>) 5,5 МБ (34,88%)
- Values.VI 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% после этой простой перезаписи. О (п) ... нет проблем! Просто избегайте бесполезного использования памяти!