Подтвердить что ты не робот

Как мне привязать long к int в hashCode()?

У меня есть ряд объектов, у которых есть поле long, значение которого однозначно идентифицирует конкретный объект во всей моей системе, подобно GUID. Я использовал Object.equals() для использования этого идентификатора для сравнения, потому что я хочу, чтобы он работал с копиями объекта. Теперь я также хочу переопределить Object.hashCode(), что в основном означает сопоставление моего long с некоторым возвращаемым значением int.

Если я правильно понял цель hashCode, она в основном используется в хеш-таблицах, поэтому было бы желательно равномерное распределение. Это означает, что достаточно просто вернуть id % 2^32. Это все, или я должен знать о чем-то еще?

4b9b3361

Ответ 1

Так как Java 8 можно использовать

Long.hashCode(guid);

Для более старых версий Java вы можете использовать следующее:

Long.valueOf(guid).hashCode();

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

Глядя на документы, оба способа используют только следующий алгоритм:

(int)(this.longValue()^(this.longValue()>>>32))

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

Ответ 2

Это немного незначительная вещь, если вы уже не используете Guava, но Guava может сделайте это для вас красиво:

public int hashCode() {
  return Longs.hashCode(id);
}

Это дает вам эквивалент Long.valueOf(id).hashCode():

return (int) (value ^ (value >>> 32));

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

return Objects.hashCode(longValue, somethingElse, ...);

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

Ответ 3

Вы правильно поняли цель hashCode. Да, желательно равномерное распределение (хотя это и не фактическое требование).

Я бы предложил ((id >> 32) ^ id).

Вышеприведенное выражение:

  • Использует все биты исходного значения, не отбрасывает информацию заранее. Например, в зависимости от того, как вы генерируете идентификаторы, верхние биты могут меняться чаще (или наоборот).
  • Не вводит никакого смещения в сторону значений с более чем одним (нулями), так как это было бы так, если бы две половины были объединены с операцией OR (AND).

Ответ 4

Java 8 добавляет Long.hashCode(long) в JDK.

Следующий код может обеспечить более высокую производительность. Этот код уменьшает вычисление до 32-разрядного int вместо вычисления с 64-разрядным long. Это может повлиять на 32-разрядную и меньшую архитектуры. 32-разрядные процессы на компьютерах x86 могли бы оптимизировать это в одну инструкцию, которая просто регистрирует XORs 2.

return (int)(value ^ (value >>> 32));

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

Ответ 5

(l >> 32) ^ l - хороший хэш-код в большинстве случаев; особенно когда длинные имеют равномерное распределение.

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

В примере, который я дал, был класс Point следующим образом:

public class Point {
    private final long coords; //x in high-bits, y in low
    public int getX() {
        return (int)(coords >> 32);
    }
    public int getY() {
        return (int)coords;
    }
    public int hashCode() {
        return (int)((coords >> 32) ^ (coords));
    }
}

Это может показаться надуманным, но иногда у вас есть несколько "полей", упакованных в длинный.

Итак, поле coords представляет 32 бита x и 32 бит y. Так почему же это проблема? Ну, это не так, если каждый из x и y равномерно распределен по их соответствующим 32 битам. Но это маловероятно на практике. Скорее всего, X и Y ограничены некоторым числом. Скажем 1024, так как это 2 ^ 10. Это означает, что не более 10 младших бит каждого X и Y установлены:

00000000 00000000 000000XX XXXXXXXX 00000000 00000000 000000YY YYYYYYYY

Возможны комбинации 2 ^ 20 (1024 * 1024). Но что делает операция hashCode?

  00000000 00000000 000000XX XXXXXXXX 
^ 00000000 00000000 000000YY YYYYYYYY
-------------------------------------
= 00000000 00000000 000000?? ????????

Существует не более 2 ^ 10 (1024) возможных значений hashCode, так как только младшие 10 бит могут быть чем-то другим, кроме нуля. Отношение хэш-значений к реальным значениям составляет 1024:(1024*1024) или 1:1024. Таким образом, сразу с места битвы существует вероятность 1/1024, что два числа имеют одинаковый хэш.

Теперь позвольте рассчитать вероятность столкновения, применив математику из проблемы рождения. Пусть p (n) - вероятность того, что с n значениями будет хотя бы одно столкновение. Мы знаем, что p (1025+) = 1, так как существует только 1024 значения.

p(n) = 1 - (n! * (1024 choose n))/1024^n

Это работает следующим образом:

n: p(n)
1: 0.00000
2: 0.00098
3: 0.00293
4: 0.00585
5: 0.00973
6: 0.01457
...
38: 0.50096
...
79: 0.95444
...
148: 0.99999

Только с 38 элементами, возможно, происходит столкновение. С 148 пунктами вероятность 99.999% (хотя бы одного) столкновения. При использовании 148 предметов каждый предмет имеет 7% -ный шанс столкнуться с другим предметом. Имея надлежащую хэширующую функцию, беря знания домена, эти числа могут легко перейти к 0.

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

Ответ 6

int result = (int)((longVal >> 32) ^ longVal);

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