Могу ли я быть уверен, что встроенный хэш для данной строки всегда один и тот же?

Я получаю строковый хэш, как это:

string content = "a very long string";
int contentHash = content.GetHashCode();

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

Я могу быть уверен, что хэш для данной строки ("очень длинная строка") будет всегда одинаков?

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

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

12 ответов


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

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

класс String переопределяет реализацию GetHashCode по умолчанию в object.

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

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

редактировать: я только что заметил, что ни один из ответов непосредственно не ссылается на каждый из вас вопросы (хотя я думаю, что ответ на них ясен), но просто убрать: -

Я могу быть уверен, что хэш для данной строки ("очень длинная строка") будет всегда одинаков?

в вашем использовании, да.

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

нет. Две разные строки могут иметь одинаковый хеш.

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

вероятность довольно низкая, в результате хэш довольно случайный из домена 4G.


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


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


Как отметили другие, хэш будет оставаться постоянным с течением времени. Но почему вы хешируете строку, а затем кладете ее как ключ в словарь? Хэши не гарантированно уникальны. Так что ваши сравнения могут быть неверными. Пусть словарь делает свою работу. Я думаю, что наиболее подходящей в этом случае является поиска HashSet.


как говорили многие другие, реализация зависит от версии фреймворка, но она также зависит от архитектура. Реализация string.GetHashCode () отличается в версиях x86 и x64 платформы, даже если они имеют одинаковый номер версии.

например, если вы пишете тип архитектуры клиент / сервер или .NET remoting и хотите использовать строковый хэш-код, чтобы остановить загрузку большого ресурса, вы можете только сделать это если оба одной и той же версии и разрядности. В противном случае вы должны использовать другой хэш -- MD5, SHA и т. д. будет работать правильно.


документация


вам не нужно догадываться о времени выполнения или версиях, просто используйте этот класс CaseInsensitiveStringComparer, который я сделал в свободное время (вы можете передать его конструктору словаря или если вы используете .NET 3.5, HashSet):

/// <summary>
/// StringComparer that is basically the same as StringComparer.OrdinalIgnoreCase, except that the hash code function is improved and guaranteed not to change.
/// </summary>
public class CaseInsensitiveStringComparer : StringComparer
{
    /// <summary>
    /// Compares two strings, ignoring case
    /// </summary>
    /// <param name="x">First string</param>
    /// <param name="y">Second string</param>
    /// <returns>Compare result</returns>
    public override int Compare(string x, string y)
    {
        return StringComparer.OrdinalIgnoreCase.Compare(x, y);
    }

    /// <summary>
    /// Checks if two strings are equal, ignoring case
    /// </summary>
    /// <param name="x">First string</param>
    /// <param name="y">Second string</param>
    /// <returns>True if strings are equal, false if not</returns>
    public override bool Equals(string x, string y)
    {
        return Compare(x, y) == 0;
    }

    /// <summary>
    /// Gets a hash code for a string, ignoring case
    /// </summary>
    /// <param name="obj">String to get hash code for</param>
    /// <returns>Hash code</returns>
    public override int GetHashCode(string obj)
    {
        if (obj == null)
        {
            return 0;
        }
        int hashCode = 5381;
        char c;
        for (int i = 0; i < obj.Length; i++)
        {
            c = obj[i];
            if (char.IsLower(c))
            {
                c = char.ToUpperInvariant(c);
            }
            hashCode = ((hashCode << 5) + hashCode) + c;
        }
        return hashCode;
    }
}

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


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

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


учитывая, что существует бесконечное количество различных строк, просто невозможно выделить другое число int (32bits, которое может представлять до 4 миллиардов) для каждого.

всего с 8 символами tehre - это 2^60 различных строк. Это бесконечно больше, чем 2^32. Естественно, хэш-код некоторых из этих строк должен конфликтовать.

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

карта.get (String key)

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

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

javadoc для объекта.hashcode делает для интересного чтения-ive вставил фрагмент ниже.

 The equals method implements an equivalence relation:

* It is reflexive: for any reference value x, x.equals(x) should return true.
* It is symmetric: for any reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
* It is transitive: for any reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
* It is consistent: for any reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the object is modified.
* For any non-null reference value x, x.equals(null) should return false. 

метод equals для объекта класса реализует наиболее различающее возможное отношение эквивалентности для объектов; то есть для любых опорных значений x и y этот метод возвращает true, если и только если x и y относятся к одному и тому же объекту (x==y имеет значение true).


могу ли я быть уверен, что хэш для данная строка ("очень длинная строка") всегда будет одно и то же?

да

могу ли я быть уверен, что два разных строки не будут иметь одинаковый хэш?

нет


Это отличный пример для пороков преждевременной оптимизации.

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

Я так не думаю. Просто используйте строку как ключ в словаре. Вот как ты должен его использовать.

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