Java hashCode из одного поля

Редактировать: Подготовить мои объекты для использования в HashMap.

Прочитав немного о том, как генерировать хеш-код, я немного запутался сейчас же. Мой (возможно, тривиальный) вопрос, Как я должен реализовать метод hashCode, когда у меня есть одно поле, которое я мог бы использовать? Могу ли я использовать поля напрямую? Если я правильно понимаю, значения для hashCode не должны изменяться в течение срока службы объекта, и у меня есть только поданный идентификатор, который соответствует этому, но я читал в другом месте, что не следует использовать идентификатор ... независимо от того, как будет выглядеть функция hashCode, основанная на этом (уникальном и неизменном) значении? Метод equals также основан только на id.

6
задан demongolem 11 February 2013 в 16:28
поделиться

3 ответа

Если ваш объект является изменяемым, то его хэш-код может меняться с течением времени. Конечно, вы должны предпочесть неизменяемые объекты ( Эффективное Java 2-е издание, Правило 15: Минимизируйте изменчивость ).

Вот рецепт хэш-кода от Джоша Блоха, из Эффективное 2-е издание Java, пункт 9: Всегда переопределяйте hashCode , когда вы переопределяете равным :

Эффективное 2-е издание Java Рецепт хэш-кода

  • Сохраните некоторое постоянное ненулевое значение, скажем 17, в переменной int , называемой result .
  • Вычислить хэш-код int c для каждого поля:
    • Если поле является логическим , вычислить (f? 1: 0)
    • Если поле имеет размер байт, char, short, int , вычислить (int) f
    • Если поле имеет длину , вычислить (int) (f ^ (f >>> 32))
    • Если поле имеет значение float , вычислить Float.floatToIntBits (f)
    • Если поле является double , вычислить Double.doubleToLongBits (f) , затем хэшировать в результате длинное , как указано выше.
    • Если поле является ссылкой на объект и метод этого класса равно сравнивает поле, рекурсивно вызывая равно , рекурсивно вызывает hashCode в поле. Если значение поля равно null , вернуть 0.
    • Если поле является массивом, обрабатывать его так, как если бы каждый элемент был отдельным полем. Если каждый элемент в поле массива имеет значение, вы можете использовать один из методов Arrays.hashCode , добавленных в выпуске 1.5.
  • Объедините хэш-код c в результат следующим образом: result = 31 * result + c;

Было бы правильно следовать рецепту как есть, даже всего с одним полем. Просто выполните соответствующее действие в зависимости от типа поля.

Обратите внимание, что есть библиотеки, которые действительно упрощают это для вас, например HashCodeBuilder из Apache Commons Lang или просто Arrays.hashCode / deepHashCode из java.util.Arrays .

Эти библиотеки позволяют вам просто написать что-то вроде этого:

@Override public int hashCode() {
    return Arrays.hashCode(new Object[] {
        field1, field2, field3, //...
    });
}

Пример Apache Commons Lang

Вот более полный пример использования построителей из Apache Commons Lang для облегчения удобного и удобочитаемого равно , hashCode , toString и compareTo :

import org.apache.commons.lang.builder.*;

public class CustomType implements Comparable<CustomType> {
    // constructors, etc
    // let's say that the "significant" fields are field1, field2, field3
    @Override public String toString() {
        return new ToStringBuilder(this)
            .append("field1", field1)
            .append("field2", field2)
            .append("field3", field3)
                .toString();
    }
    @Override public boolean equals(Object o) {
        if (o == this) { return true; }
        if (!(o instanceof CustomType)) { return false; }
        CustomType other = (CustomType) o;
        return new EqualsBuilder()
            .append(this.field1, other.field1)
            .append(this.field2, other.field2)
            .append(this.field3, other.field3)
                .isEquals();
    }
    @Override public int hashCode() {
        return new HashCodeBuilder(17, 37)
            .append(field1)
            .append(field2)
            .append(field3)
                .toHashCode();
    }
    @Override public int compareTo(CustomType other) {
        return new CompareToBuilder()
            .append(this.field1, other.field1)
            .append(this.field2, other.field2)
            .append(this.field3, other.field3)
                .toComparison();
    }
}

Эти четыре метода могут быть чрезвычайно утомительными для написания, и может быть трудно гарантировать, что все контракты соблюдаются, но, к счастью, библиотеки могут, по крайней мере, облегчить эту работу. Некоторые IDE (например, Eclipse) также могут автоматически генерировать некоторые из этих методов.

См. Также

  • Apache Commons Lang Builders
  • Эффективное 2-е издание Java
    • Правило 8: Соблюдайте общий договор при переопределении равно
    • Пункт 9: Всегда заменять hashCode , когда вы отменяете , равно
    • Пункт 10: Всегда отменять toString
    • Пункт 12: Рассмотрите возможность реализации Comparable
    • Правило 2: Рассмотрите конструктор, когда сталкиваетесь со многими параметрами конструктора
16
ответ дан 8 December 2019 в 12:17
поделиться

Если вы хотите, чтобы объекты с разными идентификаторами идентифицировались этим идентификатором, все, что вам нужно сделать, это вернуть его / сравнить.

private final int id;

public int hashCode() { return id; }

public boolean equals(Object o) { 
    return o instanceof ThisClass && id == ((ThisClass)o).id;
}
2
ответ дан 8 December 2019 в 12:17
поделиться

Не имеет значения, сколько полей используется для вычисления hashCode. Но важно работать с equals () . Если A равно B, их хэш-код должен быть таким же.

Если вы ненавидите hashCode :) и ваш объект никогда не помещается в контейнеры на основе хешей (HashMap, HashSet ..), просто оставьте hashCode () в покое, пусть его базовый класс вычислит hashCode.

1
ответ дан 8 December 2019 в 12:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: