генерация хэш-кода с уже уникальным целым числом

Простой вопрос. У меня есть объект:

class User {

    int id;
    String username;

    public User() {
    }

    public User(int id, String username) {
        this.id = id;
        this.username = username;
    }

    @Override
    public String toString() {
        return id + " - " + username;
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 31 * hash + this.id;
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final User other = (User) obj;
        return this.id == other.id;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public int getId() {
        return id;
    }
}

Чье равенство определяется на основе int id (это идентификатор базы данных).

Netbeans автоматически сгенерировал этот метод hashCode():

@Override
public int hashCode() {
    int hash = 7;
    hash = 31 * hash + this.id;
    return hash;
}

Вопрос в том, есть ли в этом какое-либо преимущество перед простым возвратом (уже) уникального int id ?

@Override
public int hashCode() {
    return id;
}

Столкновения невозможны в любом случае.

Верно?


person ryvantage    schedule 31.03.2014    source источник
comment
Цель hashCode() не в том, чтобы избежать коллизий.   -  person peter.petrov    schedule 01.04.2014
comment
В некотором смысле это зависит от того, как вы реализуете equals. Что делать, если вы хотите добавить клон значения данных?   -  person Rogue    schedule 01.04.2014
comment
Джошуа Блок: Эффективная Java, пункт 9.   -  person Boris the Spider    schedule 01.04.2014
comment
Как объясняется в книге SCJP Кэти Сиера: Идея hashCode подобна ведрам, больше уникальных хэш-кодов, больше ведер, в которые попадут ваши объекты, как только хэш-код совпадет, он будет использовать оператор равенства для проверки на равенство.   -  person Javaboy    schedule 01.04.2014


Ответы (3)


Object.hashCode() javadoc расскажет вам все, что вам нужно знать, чтобы ответить на ваш вопрос.

Общий контракт hashCode:

  • Всякий раз, когда он вызывается для одного и того же объекта более одного раза во время выполнения приложения Java, метод hashCode должен постоянно возвращать одно и то же целое число, при условии, что никакая информация, используемая в сравнениях на равенство для объекта, не изменяется. Это целое число не обязательно должно оставаться постоянным от одного выполнения приложения к другому выполнению того же приложения.

  • Если два объекта равны в соответствии с методом equals(Object), то вызов метода hashCode для каждого из двух объектов должен давать одинаковый целочисленный результат.

  • Не требуется, чтобы, если два объекта не были равны в соответствии с методом equals(java.lang.Object), то вызов метода hashCode для каждого из двух объектов должен давать разные целочисленные результаты. Однако программист должен знать, что создание различных целочисленных результатов для неравных объектов может повысить производительность хеш-таблиц.

person Mike B    schedule 31.03.2014

1) Вы можете сделать:

@Override
public int hashCode() {
    return 1; // or any int constant here
}

2) Если у вас есть, скажем, 1 000 000 таких объектов в
БД, вы могли бы сделать что-то вроде:

@Override
public int hashCode() {
    return id % 10000;
}

Таким образом, у вас будет 10 000 сегментов для 1 000 000 объектов.

3) Вы можете превратить id в Integer и просто сделать это:

@Override
public int hashCode() {
    return id.hashCode();
}

или что эквивалентно:

@Override
public int hashCode() {
    return id;
}

1) и 3) являются пограничными случаями для реализации hashCode.
Подход 2) находится где-то посередине.

person peter.petrov    schedule 31.03.2014
comment
Это сбивает с толку — зачем вам добровольно увеличивать риск столкновения в (i) или использовать хэш-код Integer, который возвращает само значение int в (ii)? - person assylias; 01.04.2014
comment
@assylias Просто хочу продемонстрировать, что обе хэш-функции действительны в случае ОП. В некотором смысле это два пограничных случая. На самом деле реальный пограничный случай — это не return id % 10000;, а return 1;. - person peter.petrov; 01.04.2014

Существует биекция между id и 31 * 7 + id, поэтому возврат id вместо этого эквивалентен. Поэтому я бы просто return id; удалил ненужные вычисления/усложнения.

Но будет ли это представлять собой совместимый метод хэш-кода? Вернемся к javadoc:

Общий контракт hashCode:

  • Всякий раз, когда он вызывается для одного и того же объекта более одного раза во время выполнения приложения Java, метод hashCode должен постоянно возвращать одно и то же целое число, при условии, что никакая информация, используемая в сравнениях на равенство для объекта, не изменяется. Это целое число не обязательно должно оставаться постоянным от одного выполнения приложения к другому выполнению того же приложения.
  • Если два объекта равны в соответствии с методом equals(Object), то вызов метода hashCode для каждого из двух объектов должен давать одинаковый целочисленный результат.
  • Не требуется, чтобы, если два объекта не были равны в соответствии с методом equals(java.lang.Object), то вызов метода hashCode для каждого из двух объектов должен давать разные целочисленные результаты. Однако программист должен знать, что создание различных целочисленных результатов для неравных объектов может повысить производительность хеш-таблиц.

В вашем случае работает?

  • (i) надеюсь, удовлетворен: вы не будете произвольно менять идентификатор данного объекта, верно?
  • (ii) если два пользователя равны, у них один и тот же хэш-код: да, потому что ваше равенство также основано на идентификаторе
  • (iii) удовлетворен
person assylias    schedule 31.03.2014