Могу ли я зависеть от значений GetHashCode(), чтобы они были согласованными?

Является ли возвращаемое значение GetHashCode() гарантированным, если предполагается, что используется одно и то же строковое значение? (С#/ASP.NET)

Я загрузил свой код на сервер сегодня, и, к моему удивлению, мне пришлось переиндексировать некоторые данные, потому что мой сервер (win2008 64-bit) возвращал разные значения по сравнению с моим настольным компьютером.

Ответы

Ответ 1

Если я не ошибаюсь, GetHashCode является согласованным, учитывая одно и то же значение, но НЕ гарантируется, что он будет согласован в разных версиях фреймворка.

Из документов MSDN в String.GetHashCode():

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

Ответ 2

У меня была аналогичная проблема, когда я заполнил таблицу базы данных информацией, которая зависела от String.GetHashCode(не лучшая идея), и когда я обновил сервер, на котором работал, на x64 я заметил значения, которые я получал от String.GetHashCode были несовместимы с тем, что уже было в таблице. Моим решением было использовать мою собственную версию GetHashCode, которая возвращает то же значение, что и String.GetHashCode, в инфраструктуре x86.

Здесь код, не забудьте скомпилировать с "Разрешить небезопасный код":

    /// <summary>
    /// Similar to String.GetHashCode but returns the same as the x86 version of String.GetHashCode for x64 and x86 frameworks.
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    public static unsafe int GetHashCode32(string s)
    {
        fixed (char* str = s.ToCharArray())
        {
            char* chPtr = str;
            int num = 0x15051505;
            int num2 = num;
            int* numPtr = (int*)chPtr;
            for (int i = s.Length; i > 0; i -= 4)
            {
                num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
                if (i <= 2)
                {
                    break;
                }
                num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1];
                numPtr += 2;
            }
            return (num + (num2 * 0x5d588b65));
        }
    }

Ответ 4

    /// <summary>
    /// Default implementation of string.GetHashCode is not consistent on different platforms (x32/x64 which is our case) and frameworks. 
    /// FNV-1a - (Fowler/Noll/Vo) is a fast, consistent, non-cryptographic hash algorithm with good dispersion. (see http://isthe.com/chongo/tech/comp/fnv/#FNV-1a)
    /// </summary>
    private static int GetFNV1aHashCode(string str)
    {
        if (str == null)
            return 0;
        var length = str.Length;
        // original FNV-1a has 32 bit offset_basis = 2166136261 but length gives a bit better dispersion (2%) for our case where all the strings are equal length, for example: "3EC0FFFF01ECD9C4001B01E2A707"
        int hash = length;
        for (int i = 0; i != length; ++i)
            hash = (hash ^ str[i]) * 16777619;
        return hash;
    }

Эта реализация может быть медленнее, чем небезопасная, опубликованная ранее. Но гораздо проще и безопаснее.

Ответ 5

Интересно, существуют ли различия между 32-битными и 64-разрядными операционными системами, потому что я уверен, что и мой сервер, и домашний компьютер работают с той же версией .NET.

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

Ответ 6

Вы используете Win2008 x86 в качестве рабочего стола? Поскольку Win2008 включает версию 2.0.50727.1434, которая представляет собой обновленную версию 2.0, включенную в RTM Vista.

Ответ 7

Не прямой ответ на ваш вопрос, на который Джонас ответил хорошо, однако это может быть полезно, если вы беспокоитесь о проверке равенства в хэшах

Из наших тестов, в зависимости от того, что вы требуете от хэш-кодов, в С#, хэш-коды не обязательно должны быть уникальными для операций Equality. В качестве примера рассмотрим следующее:

У нас было требование перегрузить оператор equals и, следовательно, функцию GetHashCode для наших объектов, поскольку они стали волатильными и неактивными, а также сами источники из данных, поэтому в одном месте приложения нам нужно было убедиться, что объект будет считаться равным другому объекту, если он был получен из одних и тех же данных, а не только в том случае, если это была одна и та же ссылка. Наши уникальные идентификаторы данных - Гиды.

Оператор equals легко обслуживался, так как мы только что проверили Guid в записи (после проверки на нуль).

Несомненно, размер данных HashCode (будучи int) зависит от операционной системы, а в нашей 32-битной системе хэш-код будет 32 бит. Математически, когда мы переопределяем функцию GetHashCode, невозможно создать уникальный хэш-код из guid, который больше 32 бит (посмотрите на него с обратной стороны, как бы вы перевели 32-битное целое число в guid?).

Затем мы выполнили некоторые тесты, в которых мы взяли Guid как строку и вернули HashCode Guid, который почти всегда возвращает уникальный идентификатор в наших тестах, но не всегда.

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

Как я уже сказал, это может быть или не иметь отношения к вашей ситуации, но если это удобный совет.

UPDATE

Чтобы продемонстрировать, у нас есть Hashtable:

Ключ: Object A (Hashcode 1), значение Object A1

Ключ: Object B (Hashcode 1), значение Object B1

Ключ: Object C (Hashcode 1), значение Object C1

Ключ: Объект D (Hashcode 2), значение Объект D1

Ключ: объект E (Hashcode 3), значение Объект E1

Когда я вызываю хэш-таблицу для объекта с ключом Object A, объект A1 будет возвращен после двух шагов, вызов для hashcode 1, а затем проверка равенства на ключевом объекте, поскольку нет уникального ключа с хэш-код 1

Когда я вызываю хэш-таблицу для объекта с ключом объекта D, объект D1 будет возвращен после 1 шага, поиск хэша

Ответ 8

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

Вот как работают поиски хэша, не так ли? Каждое ведро содержит список элементов, имеющих один и тот же хэш-код.

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

И если ваша реализация хеширования достигает хорошего распространения, этот поиск не требуется, т.е. один элемент для каждого ведра.

Правильно ли я понимаю?

Ответ 9

Мне нужно было бы сказать... вы не можете полагаться на это. Например, если я запустил хеш-код file1 через С# md5 и скопировал nd, вставьте тот же файл в новый каталог... хеш-код получился другим, даже жестким, это тот же файл. Очевидно, что эта же версия .net, то же самое. Единственное, что изменилось, это путь.