Согласованность hashCode() в строке Java
Значение hashCode строки Java вычисляется как (String.hashCode()):
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
Существуют ли какие-либо обстоятельства (например, версия JVM, поставщик и т.д.), под которым следующее выражение будет оцениваться как false?
boolean expression = "This is a Java string".hashCode() == 586653468
Обновление # 1: Если вы утверждаете, что ответ "да, есть такие обстоятельства", то, пожалуйста, дайте конкретный пример того, когда "Это строка Java".hashCode()!= 586653468. Постарайтесь как можно более конкретным/конкретным.
Обновление # 2: Мы все знаем, что, опираясь на подробности реализации hashCode(), в общем случае плохо. Тем не менее, я говорю конкретно о String.hashCode() - поэтому, пожалуйста, держите ответ на String.hashCode(). Object.hashCode() совершенно не имеет отношения к контексту этого вопроса.
Ответы
Ответ 1
Я вижу эту документацию еще в Java 1.2.
Хотя верно, что в целом вы не должны полагаться на реализацию хеш-кода, оставаясь тем же самым, теперь он документировал поведение для java.lang.String
, поэтому его изменение будет считаться нарушением существующих контрактов.
По возможности, вы не должны полагаться на хеш-коды, оставаясь одинаковыми в разных версиях и т.д., но, на мой взгляд, java.lang.String
- это особый случай просто потому, что алгоритм указан... пока вы готовы отказаться от совместимости с релизами до того, как был определен алгоритм.
Ответ 2
Я нашел что-то о JDK 1.0 и 1.1 и >= 1.2:
В JDK 1.0.x и 1.1.x hashCode функция для длинных строк, обработанных выборка каждого n-го символа. Эта довольно хорошо, что у вас будет многие строки хеширования к тому же значение, таким образом замедляя Hashtable Погляди. В JDK 1.2 функция имеет были улучшены, чтобы умножить результат пока 31, затем добавьте следующий символ в последовательности. Это немного медленнее, но намного лучше избегая столкновений. Источник: http://mindprod.com/jgloss/hashcode.html
Что-то другое, потому что вам, похоже, нужен номер: как насчет использования CRC32 или MD5 вместо хэш-кода, и вам хорошо идти - никаких обсуждений и вообще не беспокоиться...
Ответ 3
Вы не должны полагаться на хэш-код, равный определенному значению. Только то, что оно будет возвращать согласованные результаты в рамках одного и того же исполнения.
В документах API говорится следующее:
Общий контракт hashCode:
- Всякий раз, когда он вызывается одним и тем же объектом более одного раза во время выполнения приложения Java, метод hashCode должен последовательно возвращать одно и то же целое число, если информация, используемая при равных сравнениях с объектом, не изменяется. Это целое число не должно оставаться согласованным с одним исполнением приложения на другое выполнение того же приложения.
ИЗМЕНИТЬ
Поскольку javadoc для String.hashCode() указывает, как вычисляется хэш-код String, любое нарушение этого может нарушить публичную спецификацию API.
Ответ 4
Как было сказано выше, в общем случае вы не должны полагаться на хэш-код класса, который остается тем же. Обратите внимание, что даже последующие прогоны одного и того же приложения на одной виртуальной машине могут создавать разные значения хеширования. Функция AFAIK the Sun JVM вычисляет один и тот же хэш на каждом прогоне, но это не гарантируется.
Обратите внимание, что это не теоретическое. Хэш-функция для java.lang.String была изменена в JDK1.2 (у старого хэша были проблемы с иерархическими строками, такими как URL-адреса или имена файлов, поскольку он имел тенденцию создавать тот же хеш для строк, которые только отличались в конце).
java.lang.String - частный случай, так как алгоритм его hashCode() (сейчас) задокументирован, поэтому вы, вероятно, можете положиться на это. Я все равно считаю это плохой практикой. Если вам нужен хеш-алгоритм со специальными документальными свойствами, просто напишите: -).
Ответ 5
Еще одна проблема (!), о которой стоит беспокоиться, - это возможное изменение реализации ранних/поздних версий Java. Я не верю, что детали реализации заданы в камне, поэтому потенциально обновление до будущей версии Java может вызвать проблемы.
В нижней строке, я бы не полагался на реализацию hashCode()
.
Возможно, вы можете указать, какую проблему вы пытаетесь решить, используя этот механизм, и это подчеркнет более подходящий подход.
Ответ 6
Просто, чтобы ответить на ваш вопрос и не продолжать никаких обсуждений. Реализация Apache Harmony JDK, похоже, использует другой алгоритм, по крайней мере, он выглядит совершенно иначе:
Sun JDK
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
Гармония Apache
public int hashCode() {
if (hashCode == 0) {
int hash = 0, multiplier = 1;
for (int i = offset + count - 1; i >= offset; i--) {
hash += value[i] * multiplier;
int shifted = multiplier << 5;
multiplier = shifted - multiplier;
}
hashCode = hash;
}
return hashCode;
}
Не стесняйтесь проверить это самостоятельно...
Ответ 7
Если вас беспокоят изменения и, возможно, несовместимые виртуальные машины, просто скопируйте существующую реализацию hashcode в свой собственный класс утилиты и используйте это для генерации ваших хэш-кодов.
Ответ 8
Хеш-код будет рассчитываться на основе значений ASCII символов в строке.
Это реализация в классе String выглядит следующим образом
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
hash = h = isLatin1() ? StringLatin1.hashCode(value)
: StringUTF16.hashCode(value);
}
return h;
}
Столкновения в хэш-коде неизбежны. Например, строки "Ea" и "FB" дают тот же хеш-код, что и 2236