Какая коллекция .NET обеспечивает самый быстрый поиск


145

У меня есть 60 тыс. Элементов, которые нужно проверить по списку поиска из 20 тыс. Есть ли объект коллекции (например List, HashTable), который предоставляет исключительно быстрый Contains()метод? Или мне придется писать свою? Другими словами, Contains()метод по умолчанию - просто сканировать каждый элемент или использует лучший алгоритм поиска.

foreach (Record item in LargeCollection)
{
    if (LookupCollection.Contains(item.Key))
    {
       // Do something
    }
}

Примечание . Список подстановки уже отсортирован.


Contains for List не работает для списка объектов, потому что он сравнивает ссылки.
Fiur

2
Сортированные данные? Бинарный поиск - см. Ответ @Mark.
Хэмиш Смит

По моему опыту, HashtTable превосходит все до 2 млн элементов
Крис С.

Кроме того, если ваши элементы расположены в значимом порядке и довольно равномерно распределены, вы можете выполнить двоичный поиск намного быстрее, если ваши первые предположения будут в пределах предполагаемого диапазона вашего элемента. Это может иметь или не иметь какое-либо значение для вашего конкретного приложения.
Брайан

2
Не забывайте о System.Collections.Generic.SortedList (TKey, TValue), если вы хотите упростить этот материал, но избегайте хеш-набора.
Брайан

Ответы:


146

В наиболее общем случае считается System.Collections.Generic.HashSet, что структура данных "содержит" по умолчанию является рабочей лошадкой, потому что для ее оценки требуется постоянное время Contains.

Фактический ответ на вопрос «Какая самая быстрая коллекция с возможностью поиска?» Зависит от конкретного размера данных, упорядоченности, стоимости хеширования и частоты поиска.


36
Примечание: не забудьте переопределить функцию хэш-кода. Для повышения производительности предварительно сгенерируйте хэш-код в конструкторе.
Брайан

1
@ Брайан: хороший момент. Я предполагал (безосновательно), что Record.Key был каким-то встроенным типом.
Джимми

3
@Brian: вместо предварительной генерации я предпочитаю сохранить сгенерированный в первый раз, зачем замедлять конструктор чем-то, чего вы не знаете, будет ли он использоваться?
jmservera

8
К вашему сведению: Тест производительности - я создал сравнение между List <T> и HashSet <T> для строк. Я обнаружил, что HashSet примерно в 1000 раз быстрее, чем List.
Quango

10
@Quango: 3 года спустя, но на самом деле, если вы не укажете размер своего набора данных, это сравнение производительности ничего не значит: хеш-наборы имеют поиск O (1), списки имеют поиск O (n), поэтому коэффициент производительности пропорционален п.
Clément

73

Если вам не нужен заказ, попробуйте HashSet<Record>(впервые в .Net 3.5)

Если да, используйте List<Record>и позвоните BinarySearch.


8
Или, в .NET> = 4, использование SortedSet
StriplingWarrior

2
Или еще лучше, ImmutableSortedSetот System.ImmutableCollections
Alexei S

24

Вы думали List.BinarySearch(item)?

Вы сказали, что ваша большая коллекция уже отсортирована, так что это прекрасная возможность? Хеширование определенно будет самым быстрым, но это вызывает свои проблемы и требует гораздо больше накладных расходов на хранение.


1
Вы правы, хеш может вызвать нежелательные проблемы при использовании изменяемых объектов в качестве ключа.
jmservera

10

Вы должны прочитать этот блог, что скорость протестировала несколько различных типов коллекций и методов для каждого, используя как однопоточные, так и многопоточные методы.

Согласно результатам, BinarySearch on a List и SortedList были лучшими исполнителями, постоянно сталкиваясь с трудностями при поиске чего-либо в качестве «ценности».

При использовании коллекции, допускающей использование «ключей», Dictionary, ConcurrentDictionary, Hashset и HashTables показали лучшие результаты в целом.


4

Храните оба списка x и y в отсортированном порядке.

Если x = y, выполните свое действие, если x <y, продвиньте x, если y <x, продвиньте y, пока любой из списков не станет пустым.

Время прохождения этого пересечения пропорционально min (размер (x), размер (y))

Не запускайте цикл .Contains (), он пропорционален x * y, что намного хуже.


+1 за более эффективный алгоритм. Даже если списки в настоящее время не отсортированы, было бы более эффективно сначала отсортировать их, а затем запустить этот алгоритм.
Matt Boehm

Разве время выполнения не будет пропорционально max (size (x), size (y)) в худшем случае? Пример: int [] x = {99,100}; int [] y = {0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
Мэтт Бём

Нет, потому что после завершения меньшего набора вы можете добавить оставшиеся элементы из большего набора, потому что они уже отсортированы. Я думаю, что этот процесс похож на сортировку слиянием.

3

Если есть возможность отсортировать элементы, есть гораздо более быстрый способ сделать это, чем поиск ключей в хеш-таблице или b-дереве. Хотя, если ваши предметы не сортируются, вы все равно не сможете поместить их в b-дерево.

В любом случае, если оба списка сортируются с возможностью сортировки, это просто вопрос обхода списка поиска по порядку.

Walk lookup list
   While items in check list <= lookup list item
     if check list item = lookup list item do something
   Move to next lookup list item

Да, верно. Если у вас есть два отсортированных списка, вам нужно пройти по каждому только один раз.
denver

3

Если вы используете .Net 3.5, вы можете сделать более чистый код, используя:

foreach (Record item in LookupCollection.Intersect(LargeCollection))
{
  //dostuff
}

У меня здесь нет .Net 3.5, поэтому это не проверено. Он полагается на метод расширения. Не то чтобы LookupCollection.Intersect(LargeCollection)это, вероятно, не то же самое, что LargeCollection.Intersect(LookupCollection)... последнее, вероятно, намного медленнее.

Предполагается, что LookupCollection является HashSet


2

Если вы не беспокоитесь о писке каждой последней бит производительности, предложение использовать HashSet или двоичный поиск является твердым. Ваши наборы данных недостаточно велики, чтобы это было проблемой в 99% случаев.

Но если это всего лишь один из тысяч раз, когда вы собираетесь это сделать, а производительность критична (и доказано, что это неприемлемо с использованием HashSet / двоичного поиска), вы, безусловно, могли бы написать свой собственный алгоритм, который проходил бы отсортированные списки, выполняя сравнения по мере вашего продвижения. Каждый список будет просматриваться не более одного раза, и в патологических случаях было бы неплохо (если бы вы пошли по этому маршруту, вы, вероятно, обнаружили бы, что сравнение, предполагая, что это строка или другое нецелое значение, будет реальными расходами и что оптимизация будет следующим шагом).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.