HashSet <T> порівняно зі словником <K, V> wrt час пошуку, щоб знайти, чи існує елемент


103
HashSet<T> t = new HashSet<T>();
// add 10 million items


Dictionary<K, V> t = new Dictionary<K, V>();
// add 10 million items.

Чий .Containsметод швидше повернеться?

Просто для уточнення, моя вимога полягає в тому, що у мене є 10 мільйонів об'єктів (ну, насправді рядків), які мені потрібно перевірити, чи вони існують у структурі даних. Я НІКОЛИ не повторюю.


1
Крок 1. Перевірте, чи обидва роблять одне і те ж (у цьому випадку дві колекції призначені для різних цілей) Крок 2: Перегляньте документацію і перевірте, чи відчуваєте ви добре їх асимптотичну складність. Крок 3: Якщо ви відчуваєте, що потрібно більше турбуватися, виміряйте себе, а потім задайте питання, розміщуючи еталон разом із ним. У вашому випадку на першому кроці питання стає безглуздим.
nawfal

Відповіді:


153

Тест продуктивності HashSet vs List vs Dictionary, взятий звідси .

Додати 1000000 об’єктів (без перевірки дублікатів)

Містить чек для половини об'єктів колекції 10000

Видаліть половину предметів колекції 10000


9
Чудовий аналіз! Схоже, що .Contains for Dictionary настільки швидкий, що взагалі немає користі від використання HashSet, у випадку з ОП.
EtherDragon

2
так, у мене було те саме питання, що і до ОП. У мене вже є словник, який я використовую з інших причин, і хотів дізнатися, чи мені вигідно перейти на хешсет замість використання ContainsKey. Схоже, відповіді немає, оскільки обоє так швидко.
FistOfFury

4
На противагу тому, що попередні коментарі, мабуть, передбачають, так, ви повинні перейти на HashSet, оскільки він дає вам те, що ви хочете: зберігання набору значень (на відміну від підтримки певного відображення). Ця відповідь вказує на те, що в порівнянні зі словником не буде негативного впливу на продуктивність.
Франсуа Босьє

Ця відповідь НЕ говорить вам про порівняння характеристик HashSet і словника ... все це говорить вам про те, що вони обоє швидше, ніж Список ... ну ... так! Очевидно! HashSet може бути в 3 рази швидшим, і ви цього не знаєте, оскільки релевантний тест обвалився як "миттєвий ... порівняно зі списком ".
Брондаль

71

Я припускаю, що ви маєте Dictionary<TKey, TValue>на увазі у другому випадку? HashTable- це негенеричний клас.

Ви повинні вибрати правильну колекцію для роботи виходячи з ваших фактичних вимог. Ви дійсно хочете зіставити кожен ключ із значенням? Якщо так, використовуйте Dictionary<,>. Якщо ви тільки піклуватися про нього як набір, використання HashSet<>.

Я би сподівався HashSet<T>.Containsі Dictionary<TKey, TValue>.ContainsKey(які є порівнянними операціями, якщо припустити, що ви грамотно використовуєте свій словник) в основному виконувати те саме - вони принципово використовують той же алгоритм. Я вважаю, що при Dictionary<,>більшій кількості записів у вас з’являється більша ймовірність видути кеш, Dictionary<,>ніж з HashSet<>, але я б очікував, що це буде незначним порівняно з болем вибору неправильного типу даних просто з точки зору того, що ви намагаються досягти.


Так, я мав на увазі словник <TKey, TValue>. Мене хвилює лише пошук існування елемента в структурі даних, тобто все .
halivingston,

3
@halivingston У такому випадку використовуйте HashSet. Це дає зрозуміти, що це все, що вам потрібно.
Джон Скіт

2
Добре, дякую. Насправді у мене зараз є HashSet <TKey>, і копія Словника <Tkey, TValue> також у пам'яті. Я спочатку .Contains на HashSet, потім отримую значення у словнику <TKey, TValue>. Зараз у мене є нескінченна пам'ять, але незабаром я побоююся, що моя пам’ять буде обмежена, і наша команда попросить мене видалити цей дублікат у пам'яті, і тоді я змушений буде використовувати Словник <TKey, TValue>.
Галівінгстон,

4
Ви знаєте, що Словник занадто правильно має функцію ContainsKey? Чому ти дублюєш дані?
Сліпий

8
Якщо у вас вже є дані у словнику, то ваш перший коментар явно невірний - вам потрібно також пов’язати ключі зі значеннями. Можливо, не для цього конкретного біта коду, але це не має значення. Якщо у вас вже є Dictionaryінші причини, вам слід скористатися цим.
Джон Скіт

7

З документації MSDN для словника <TKey, TValue>

"Отримання значення за допомогою його ключа дуже швидко, близьке до O (1) , тому що клас Словник реалізований як хеш-таблиця. "

З приміткою:

"Швидкість пошуку залежить від якості алгоритму хешування типу, визначеного для TKey"

Я знаю, що ваше запитання / повідомлення давнє, але, шукаючи відповіді на подібне запитання, я натрапив на це.

Сподіваюся, це допомагає. Прокрутіть униз до розділу Зауваження, щоб отримати докладнішу інформацію. https://msdn.microsoft.com/en-us/library/xfhwa508(v=vs.110).aspx


4

Це різні структури даних. Також немає загальної версії HashTable.

HashSetмістить значення типу T, які HashTable(або Dictionary) містять пари ключ-значення. Тож вам слід вибрати збір, на основі яких даних потрібно зберігати.


0

Прийнята відповідь на це запитання НЕ коректно відповідає на запитання! Це трапляється, щоб дати правильну відповідь, але ця відповідь не відображається в наданих ними доказах.

Відповідь показує, що пошук ключів на a Dictionaryабо HashSetнабагато швидший, ніж пошук в a List. Що правда, але не цікаво, ні дивно, ні доказ того, що вони однакові швидкість.

Я запустив код нижче, щоб порівняти часи пошуку, і я можу зробити висновок, що вони справді однакові. (Або принаймні, якщо є якась різниця, то різниця знаходиться в межах стандартного відхилення цієї швидкості)

Зокрема, для цього тесту 100 000 000 пошукових записів займали від 10 до 11,5 секунд для обох, для мене.

Код тесту:

private const int TestReps = 100_000_000;
[Test]
public void CompareHashSetContainsVersusDictionaryContainsKey()
{
    for (int j = 0; j < 10; j++)
    {
        var rand = new Random();
        var dict = new Dictionary<int, int>();
        var hash = new HashSet<int>();

        for (int i = 0; i < TestReps; i++)
        {
            var key = rand.Next();
            var value = rand.Next();
            hash.Add(key);
            dict.TryAdd(key, value);
        }

        var testPoints = Enumerable.Repeat(1, TestReps).Select(_ => rand.Next()).ToArray();
        var timer = new Stopwatch();
        var total = 0;
        
        timer.Restart();
            for (int i = 0; i < TestReps; i++)
            {
                var newKey = testPoints[i];
                if (hash.Contains(newKey))
                {
                    total++;
                }
            }
        Console.WriteLine(timer.Elapsed);
        
        var target = total;
        Assert.That(total == target);
        

        timer.Restart();
            for (int i = 0; i < TestReps; i++)
            {
                var newKey = testPoints[i];
                if (dict.ContainsKey(newKey))
                {
                    total++;
                }
            }
        Console.WriteLine(timer.Elapsed);

        Assert.That(total == target * 2);
        Console.WriteLine("Set");
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.