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

Что такое hashCode для пользовательского класса, имеющего только два свойства int?

В Java у меня есть класс, который представляет точку с int-координатами

public class Point {
    int x = -1;
    int y = -1;

    public Point (int xNew, int yNew) {
        x = xNew; y = yNew;
    }

    public boolean equals (Object o) {
        // no need for (o instanceof Point) by design
        return x == ((Point)o).x && y == ((Point)o).y;
    }
}

Я использую объекты класса Point в качестве ключей в HashMap и как элементы в HashSet.

Какой лучший кандидат для функции hashCode? Я бы сделал его двойным, так что левая часть равна x, а правая часть - y, например: x = 4, y = 12, то hashCode возвращает 4.12. Но по реализации он не может быть двойным, только int.

Это не вариант:

public int hashCode() {
    // no need to check for exception parseInt since x and y are valid by design
    return Integer.parseInt(Integer.toString(x) + Integer.toString(y));
}

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

4b9b3361

Ответ 1

Вы не можете изменить тип hashCode и не хотите.

Я бы просто пошел с чем-то вроде:

public int hashCode() {
    return x * 31 + y;
}

Обратите внимание, что это означает, что (a, b) в большинстве случаев отличается от (b, a) (в отличие от добавления или XOR-ing). Это может быть полезно, если вы часто получаете ключи для "переключаемых" значений в реальной жизни.

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

В общем, я обычно следую тому же шаблону, который предлагает Джош Блох в "Эффективной Java":

public int hashCode() {
    int hash = 17;
    hash = hash * 31 + field1Hash;
    hash = hash * 31 + field2Hash;
    hash = hash * 31 + field3Hash;
    hash = hash * 31 + field4Hash;
    ...
    return hash;
}

Где field1Hash будет хеш-кодом для полей ссылочного типа (или 0 для нулевой ссылки), сам int для значений int, некоторый хэш от 64 бит до 32 для long и т.д.

EDIT: Я не помню подробностей о том, почему 31 и 17 работают хорошо вместе. Тот факт, что они оба простые, может быть полезен, но из того, что я помню, математика, почему хеши, подобные этому, в целом разумны (хотя и не так хороши, как хеши, где распространение вероятных значений известно заранее) либо трудны, либо не совсем понятно. Я знаю, что умножение на 31 дешево (сдвиг влево 5 и вычесть исходное значение)...

Ответ 2

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

Насколько мне известно, наилучшее отображение из Z & sup2; → Z - это "элегантная функция спаривания" (google it). Вот реализация

// x,y must be non-negative
int elegant(int x, int y) {
    return x < y ? y * y + x : x * x + x + y;
}


// returns a unique number for every x,y pair
int elegantSigned(int x, int y) {
    if (x < 0) {
        if (y < 0)
            return 3 + 4 * elegant(-x - 1, -y - 1);
        return 2 + 4 * elegant(-x - 1, y);
    }
    if (y < 0)
        return 1 + 4 * elegant(x, -y - 1);
    return 4 * elegant(x, y);
}

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

Ответ 3

Просто используйте java.util.Objects.hash(Object... values).

public int hashCode() {
    return Objects.hash(field1,field2);
}

Objects.hash фактически вызывает Arrays.hashCode(Object a [])

public static int hashCode(Object a[]) {
    if (a == null)
        return 0;

    int result = 1;

    for (Object element : a)
        result = 31 * result + (element == null ? 0 : element.hashCode());

    return result;
}

Ответ 4

Часто стоит рассмотреть Apache Commons HashCodeBuilder

Этот класс позволяет создать хороший метод hashCode для любого класса. Он следует правилам, изложенным в книге "Эффективная Ява" Джошуа Блох. Написание хорошего метода hashCode на самом деле довольно сложно. Этот класс предназначен для упрощения процесса

и я определенно рекомендую посмотреть справочную книгу Effective Java.

Ответ 5

Существует общая стратегия генерации операции hashcode. В вашем случае это будет:

public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + x;
    result = prime * result + y;
    return result;

}

Ответ 7

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

  1. Objects.hashCode(...) свободно и понятно, что нужно сделать, НО он использует varargs (неявно создающий массив) и, кроме того, он неявно упаковывает каждый отдельный примитив, передаваемый в метод.
  2. x * 31 + y эффективен с точки зрения производительности: не используется бокс, не используются явные или неявные операции создания массива. НО неясно, что нужно делать. Почему 31, а не 42? Для тех, кто знаком с тем, как работает хеширование, нетрудно понять такой код, но зачем другим? Второй недостаток заключается в том, что его сложно расширить: вы легко можете забыть добавить новые значения в хеш-код, если вы, например, хотите перейти в 3D и добавить координату z, потому что это вынуждает вас копировать и вставлять практически идентичный код. раз.

Я могу представить третий подход, не упомянутый в ответах выше:

@Override
public final int hashCode()
{
    final int[] numbers = {x, y};
    return Arrays.hashCode(numbers);
}

Он использует временный массив для хранения хэшированных целых чисел и вызывает Arrays.hashCode(), который доступен с Java 1.5, также есть версии для других примитивных типов.

Плюсы: СУХОЙ, беглой и совершенно ясно, что нужно сделать. Он не страдает от неявного бокса и не использует неявный vararg. Это относительно быстро и дешево. Его можно легко расширить, добавив дополнительные числа в инициализатор массива.

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

С наилучшими пожеланиями.

Ответ 8

попробуйте добавить свои хэш-коды.?

return new Integer (x).hashCode() + новый Integer (y).hashCode();