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

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

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

Ответ 1

Если ваш объект изменен, тогда приемлемо, чтобы его хэш-код менялся со временем. Конечно, вы должны предпочесть непреложные объекты (Effective Java 2nd Edition, Item 15: Minimize mutability).

Вот рецепт хэш-кода от Джоша Блоха, из Effective Java 2nd Edition, Item 9: Всегда переопределяйте hashCode при переопределении equals:

Эффективный рецепт хеш-кода Java 2nd Edition

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

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

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

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

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

Пример Apache Commons Lang

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

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

public class CustomType implements Comparable<CustomType> {
    // constructors, etc
    // let 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
  • Эффективное Java 2nd Edition
    • Пункт 8: соблюдать общий контракт при переопределении equals
    • Пункт 9: Всегда переопределять hashCode при переопределении equals
    • Пункт 10: Всегда переопределять toString
    • Пункт 12: Рассмотрите возможность выполнения Comparable
    • Пункт 2. Рассмотрим конструктор, столкнувшись со многими параметрами конструктора.

Ответ 2

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

private final int id;

public int hashCode() { return id; }

public boolean equals(Object o) { 
    return o instanceof ThisClass && id == ((ThisClass)o).id;
}

Ответ 3

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

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