Ответ 1
Нет, вам не нужно иметь реализацию, которая возвращает уникальное значение "очевидно", так как очевидно, что большинство реализаций будет нарушено.
То, что вы хотите сделать, состоит в том, чтобы иметь хороший разброс по битам, особенно для общих значений (если любые значения более распространены, чем другие). Запрет специальных знаний о вашем формате, то лучше всего использовать хэш-код самой строки.
С особым знакомством с пределами вашего формата id, возможно, будет возможно настроить и обеспечить лучшую производительность, хотя ложные предположения, скорее всего, сделают вещи хуже, чем лучше.
Изменить: при хорошем разбросе бит.
Как указано здесь и в других ответах, быть совершенно уникальным невозможно, и возможны хеш-столкновения. Методы использования хэша знают об этом и могут справиться с этим, но это влияет на производительность, поэтому мы хотим, чтобы столкновения были редкими.
Кроме того, хеши, как правило, повторно хешируются, поэтому наше 32-разрядное число может быть уменьшено до, например, один в диапазоне от 0 до 22, и мы хотим как можно лучше распределить его по возможности.
Мы также хотим сбалансировать это, не задумываясь так долго, чтобы вычислить наш хэш, что он становится узким местом сам по себе. Неправильный балансирующий акт.
Классический пример плохого хеш-метода - это один из координированных паров X, Y ints, который делает:
return X ^ Y;
Хотя это делает хорошую работу по возврату 2 ^ 32 возможных значений из 4 ^ 32 возможных входов, в реальном мире довольно часто встречаются множества координат, где X и Y равны ({0, 0}, {1, 1}, {2, 2} и т.д.), Которые все хеш равны нулю, или совпадающие пары ({2,3} и {3, 2}), которые будут иметь хэш с тем же номером. Скорее всего, нам лучше обслуживать:
return ((X << 16) | (x >> 16)) ^ Y;
Теперь есть столько же возможных значений, для которых это ужасно, чем для первого, но оно, как правило, лучше работает в реальных случаях.
Конечно, есть другая работа, если вы пишете класс общего назначения (не знаете, какие возможные входы есть) или лучше понимаете цель. Например, если я использовал объекты Date, но знал, что все они будут только датами (временная часть всегда полночь) и только в течение нескольких лет друг от друга, то я могу предпочесть специальный хеш-код, который использовал только день, месяц и более низкие цифры лет, над стандартным. Писатель Date
хотя и не может работать над такими знаниями и должен стараться удовлетворить всех.
Следовательно, если я, например, знал, что данная строка всегда будет состоять из 6 нечувствительных к регистру символов в диапазоне [az] или [0-9] (что кажется вам, но это не ясно из ваш вопрос, что он делает), тогда я мог бы использовать алгоритм, которому присваивалось значение от 0 до 35 (36 возможных значений для каждого символа) каждому символу, а затем проходил через строку, каждый раз умножая текущее значение на 36 и добавляя значение следующего char.
Предполагая хорошее распространение в идентификаторах, это был бы путь, особенно если бы я сделал такой порядок, чтобы младшие значащие цифры в моем хеше соответствовали наиболее часто меняющимся char в id (если такой вызов может быть сделан), следовательно, выживать повторное хеширование до меньшего диапазона.
Однако, не имея таких знаний о формате, я не могу сделать этот вызов с уверенностью, и я мог бы сделать что-то хуже (медленный алгоритм для незначительного или даже отрицательного выигрыша в хэш-качестве).
Одно из преимуществ заключается в том, что, поскольку он является идентификатором в себе, то, по-видимому, ни один другой неравный объект не имеет одинакового идентификатора, и, следовательно, никаких других свойств не требуется. Это не всегда выполняется.