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

Почему Netbeans генерирует hashCode() так, как он это делает?

Я развиваюсь на Java с Netbeans уже некоторое время, и есть некоторые вещи, на которые я просто полагаюсь, работая, не подвергая сомнению, как это сделать. Среди них - автоматически генерируемые методы hashCode() и equals().

Метод equals просто следовать, но я нахожу метод hashCode несколько загадочным. Я не понимаю, почему он выбирает множители и применяет операции, которые он выполняет.

import java.util.Arrays;
import java.util.Objects;

public class Foo {

    int id;
    String bar;
    byte[] things;

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 89 * hash + this.id;
        hash = 89 * hash + Objects.hashCode(this.bar);
        hash = 89 * hash + Arrays.hashCode(this.things);
        return hash;
    }    
}

Поиск документации, этого сайта и Google для таких вещей, как "netbeans generate hashcode", не отображал ничего, что казалось актуальным. Кто-нибудь знает, что такое стратегия поколения и почему Netbeans использует его?

Edit:
Спасибо за ответы до сих пор! Особенно из-за этого ответа на связанном вопросе SO, я понимаю логику использования простых чисел при разработке метода hashCode намного более полно. Тем не менее, другим аспектом моего вопроса, который до сих пор никто не обращал до конца, является то, как и почему Netbeans выбирает простые числа, которые он делает для своих сгенерированных методов. Поле hash и другой множитель (89 в моем примере) кажутся разными в зависимости от различных факторов класса.

Например, если я добавлю второй класс String в класс, hashCode() станет

public int hashCode() {
    int hash = 7;
    hash = 13 * hash + this.id;
    hash = 13 * hash + Objects.hashCode(this.bar);
    hash = 13 * hash + Objects.hashCode(this.baz);
    hash = 13 * hash + Arrays.hashCode(this.things);
    return hash;
}

Итак, почему Netbeans выбирает эти конкретные простые числа, в отличие от других?

4b9b3361

Ответ 1

Это оптимизация, направленная на лучшее распределение хэш-значений. Eclipse делает это аналогично. Посмотрите Зачем использовать простое число в hashCode? и Почему хэш-код Java в String использует 31 как множитель?.

Это никоим образом не требуется. Даже return 0; достаточно для выполнения контракта equals/hashcode. Единственная причина заключается в том, что структуры данных, основанные на хешах, лучше работают с хорошими распределенными хеш-значениями.

Некоторые назвали бы эту преждевременную оптимизацию. Я думаю, это нормально, так как его а) бесплатно (сгенерировано) и b) широко признано (почти каждая IDE делает это).

Ответ 2

В IBM статья о том, как написать свои собственные методы equals() и hashCode(). То, что они делают, прекрасно, хотя 31 имеет тенденцию быть лучшим премьер, потому что умножение может быть оптимизировано лучше.

Также смотрите как работает String.hashCode(). Это точно такое, но с разными типоразмерами и однородными типами.

Ответ 3

Из статьи Джошуа Блоха, статья 9 из "Эффективная Java" 2-е изд., важно помнить, что всегда переопределять hashCode(), когда вы переопределяете equals(), чтобы гарантировать, что равные объекты будут иметь одинаковые хэш-коды, иначе вы могли бы легко нарушить этот договор. Хотя он говорит, что современная хэш-функция является темой для докторских исследований, рецепт, который он дает для хорошего общего назначения, hashCode может в вашем случае получить:

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + id;
    result = 31 * result + bar.hashCode();
    result = 31 * result + Arrays.hashCode(things);
    return result ;
}  

Как уже упоминалось @zapl и David Ehrmann, компилятор может легко оптимизировать умножение 31 на операцию смены бит и минус 1, так что это может быть немного быстрее, если это важно.