hashCode() и equals() и мои примеры

121
16 ноября 2020, 03:40

Прочитал статью на Хабре и вот мои примеры и информация оттуда:

Мой код:

public class Man {
    private String noseSize;
    private String eyesColor;
    private String haircut;
    private boolean scars;
    private int dnaCode;
    public Man(String noseSize, String eyesColor, String haircut, boolean scars, int dnaCode) {
        this.noseSize = noseSize;
        this.eyesColor = eyesColor;
        this.haircut = haircut;
        this.scars = scars;
        this.dnaCode = dnaCode;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Man man = (Man) o;
        return dnaCode == man.dnaCode;
    }
//геттеры сеттеры и тд

Сперва, что-бы избежать путаницы, определимся с терминологией. Одинаковые объекты — это объекты одного класса с одинаковым содержимым полей.

для одного и того-же объекта, хеш-код всегда будет одинаковым;

public class Test {
    public static void main(String[] args) {
        Man man1 = new Man("Большой", "Зеленый", "Горшок", true, 13);
        Man man2 = new Man("Большой", "Зеленый", "Горшок", true, 13);
        System.out.println(man1.hashCode());
    }
}

Окей здесь всё правильно и сколько раз перезапускал показывает один и тот же хеш код, идем дальше:

если объекты одинаковые, то и хеш-коды одинаковые

public class Test {
    public static void main(String[] args) {
        Man man1 = new Man("Большой", "Зеленый", "Горшок", true, 13);
        Man man2 = new Man("Большой", "Зеленый", "Горшок", true, 13);
        System.out.println(man1.hashCode() + "  " + man2.hashCode());
    }
}

Вывод в консоль: 1802598046 659748578 Почему здесь и два одинаковых хеш кода? Идем дальше:

если хеш-коды равны, то входные объекты не всегда равны (коллизия)

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

если хеш-коды разные, то и объекты гарантированно разные; Еще раз возвращаюсь к этому примеру где хеш коды были разными, но объекты идентично одинаковые, вот что вывелось в консоль:

1802598046  659748578

 public class Test {
        public static void main(String[] args) {
            Man man1 = new Man("Большой", "Зеленый", "Горшок", true, 13);
            Man man2 = new Man("Большой", "Зеленый", "Горшок", true, 13);
            System.out.println(man1.hashCode() + "  " + man2.hashCode());
        }
    }
Answer 1

Вас тут все равно немного запутали

для одного и того-же объекта, хеш-код всегда будет одинаковым;

по умолчанию hashCode возвращает одинаковое значение для одних и тех же объектов но не для одинаковых объектов.

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

Если почитать javadoc к методу hashcode то можно узнать что обычно, hashcode это адрес объекта в куче, приведенный к целому числу(Integer)

As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)

Это достаточно важно, если учесть что эти методы (equals() и hashСode()) используются повсеместно внутри например Java коллекций, для того чтобы определить эквивалентность объектов. Причем крайне важно обеспечить хорошую функцию hashСode(), в противном случае возможны нежелательные коллизии.например с плохой функцией hashСode() объекты внутри HashMap будут чаще попадать в одну корзину, чем ухудшают общую производительность поиска по ключу в этой HashMap, хоть и есть встроенный механизм защиты от этого

jdk1.8.0_131\src.zip!\java\util\HashMap.java

/**
 * Computes key.hashCode() and spreads (XORs) higher bits of hash
 * to lower.  Because the table uses power-of-two masking, sets of
 * hashes that vary only in bits above the current mask will
 * always collide. (Among known examples are sets of Float keys
 * holding consecutive whole numbers in small tables.)  So we
 * apply a transform that spreads the impact of higher bits
 * downward. There is a tradeoff between speed, utility, and
 * quality of bit-spreading. Because many common sets of hashes
 * are already reasonably distributed (so don't benefit from
 * spreading), and because we use trees to handle large sets of
 * collisions in bins, we just XOR some shifted bits in the
 * cheapest possible way to reduce systematic lossage, as well as
 * to incorporate impact of the highest bits that would otherwise
 * never be used in index calculations because of table bounds.
 */
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

В Вашем примере

 Man man1 = new Man("Большой", "Зеленый", "Горшок", true, 13);
 Man man2 = new Man("Большой", "Зеленый", "Горшок", true, 13);

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

Если переопределить equals() и hashСode() соответствующим образом то такие объекты будут считаться одним объектом.

public class Man {
    private String noseSize;
    private String eyesColor;
    private String haircut;
    private boolean scars;
    private int dnaCode;
    public Man(String noseSize, String eyesColor, String haircut, boolean scars, int dnaCode) {
        this.noseSize = noseSize;
        this.eyesColor = eyesColor;
        this.haircut = haircut;
        this.scars = scars;
        this.dnaCode = dnaCode;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Man man = (Man) o;
        if (scars != man.scars) return false;
        if (dnaCode != man.dnaCode) return false;
        if (noseSize != null ? !noseSize.equals(man.noseSize) : man.noseSize != null) return false;
        if (eyesColor != null ? !eyesColor.equals(man.eyesColor) : man.eyesColor != null) return false;
        return haircut != null ? haircut.equals(man.haircut) : man.haircut == null;
    }
    @Override
    public int hashCode() {
        int result = noseSize != null ? noseSize.hashCode() : 0;
        result = 31 * result + (eyesColor != null ? eyesColor.hashCode() : 0);
        result = 31 * result + (haircut != null ? haircut.hashCode() : 0);
        result = 31 * result + (scars ? 1 : 0);
        result = 31 * result + dnaCode;
        return result;
    }
}

PS: В приличных IDE есть функция, которая помогает сгенерировать этот код.

я пользуюсь IntellijIdea community, там это делается Alt+Insert > equals() and hashCode()

READ ALSO
Как обновить текст тега?

Как обновить текст тега?

На javafx создал приложение, добавил браузер javafxscene

104
Ajax. Выполнение без перезагрузки страницы

Ajax. Выполнение без перезагрузки страницы

У меня есть ui с помощью которого я могу удалять, редактировать, добавлять записи в бдНа главной странице просто таблица с соответствующими...

92
Разъясните на пальцах как работает эта строка

Разъясните на пальцах как работает эта строка

Задача: написать функцию, которая принимает строку скобок, и определить, является ли порядок скобок действительным['(',')'] - это действительный...

96