Является ли HashSet <T> самым быстрым контейнером для поиска?

Мне нужно проверить, что определенная строка содержится в наборе других:

private bool Contains(string field)
{
   return this.Fields.Contains(field); // HashSet<string> local property
}

Каков наилучший тип контейнера для использования, если только одна его задача - удерживать несколько строк и проверять, является ли другой или нет?

Ответ 1

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

Ответ 2

Работает ли HashSet? Конечно. Но это не вопрос, который вы задали. Вы попросили максимально быстрый поиск.

Насколько это возможно? Нет, конечно, нет, ни в какой мере.

Прежде всего, чтобы говорить о "самом быстром", нам нужно точно описать, что означает "самый быстрый". Вы имеете в виду:

  • самый маленький наихудший возможный случай время
  • наименьшее среднее время усреднения по многим таймингам
  • наименьшее среднее время с учетом конкретного шаблона использования
  • что-то еще

? Просьба уточнить, что означает "самый быстрый". Мы можем разработать алгоритм, который является теоретически максимально быстрым, только если мы точно знаем, какие самые быстрые средства для вас могут быть.

Например, предположим, что вы пишете компилятор. Что-то, что мы должны делать все время в компиляторах, это проверить, находится ли конкретная строка в списке строк. Возможно, мы проверяем, является ли строка ключевым словом, поэтому нам нужно выяснить, находится ли данная строка внутри набора { "int", "double", "for", "foreach", "class"... }

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

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

Рассмотрим, например, если вы пишете реализацию среды выполнения JScript. Мы часто должны искать строку в наборе строк:

for(i = 0; i < 10; ++i) { foo.bar(i); }

Здесь мы должны искать строку "bar" внутри объекта, идентифицированного "foo" десять раз. Хэш-таблица внутри "foo", которая реализует этот поиск, замечает первый раз через цикл, что "бар" используется, поэтому он динамически изменяет структуру хеш-таблицы, так что второй раз через цикл поиск выполняется быстрее. Это стратегия, которую мы использовали в нашей реализации JScript.

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

for(i = 0; i < 10; ++i) { foo.bar(i); foo.blah(i); foo.abc(i); }

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

К счастью для нас, мы не были, как и вы, искали быстрый поиск. Мы только искали достаточно быстрый поиск.

Можете ли вы тщательно и полностью описать, что именно вы используете для быстрого поиска? Существует множество алгоритмов, которые вы можете использовать для ускорения поиска, но они очень сложны.