Должны ли объекты, которые имеют одинаковое значение ha sh, равны? - PullRequest
1 голос
/ 13 марта 2020

В этом примере ниже я создаю два объекта, которые имеют абсолютно одинаковую внутреннюю структуру. Оба содержат только значение 1 в качестве переменной экземпляра. Я думаю, что если я возьму ха sh из e1, он должен быть таким же, как ха sh из e2 и, следовательно, e1.equals(e2) должен вернуть true.

class EqualsChecker {

    public static void main(String[] args) {

        Elem e1 = new Elem(1);
        Elem e2 = new Elem(1);


        System.out.println(e1);                                // EqualsChecker$Elem@6ff3c5b5
        System.out.println(e2);                                // EqualsChecker$Elem@3764951d
        System.out.println("e1.equals(e2): " + e1.equals(e2)); // returns false
    }


    static class Elem {
        private int v;
        public Elem(int i) {
            this.v = i;
        }   
    }   
}

Почему equals возвращает здесь false? Я думаю, что у меня есть средний регистр на рисунке ниже: enter image description here

Ответы [ 3 ]

2 голосов
/ 13 марта 2020
Реализация

equals(Object) по умолчанию проверяет, являются ли два объекта одним и тем же экземпляром (т.е. они ==). Если вам нужна другая логика c, вам придется реализовать ее самостоятельно. Обратите внимание, что если вы сделаете это, вы также должны реализовать свой собственный hashCode(), чтобы два равных объекта также имели соответствующие коды ha sh. Например:

class Elem {
    private int v;

    @Override
    public boolean equals(final Object o) {
        if (o == null || this.getClass() != o.getClass()) {
            return false;
        }
        Elem elem = (Elem) o;
        return this.v == elem.v;
    }

    @Override
    public int hashCode() {
        return this.v;
    }
}
1 голос
/ 13 марта 2020

Посмотрите на следующие пункты из https://docs.oracle.com/javase/9/docs/api/java/lang/Object.html#hashCode -

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

Теперь рассмотрим следующий код и его вывод:

class MyEmployee {
    String code;
    String name;
    int age;

    public MyEmployee(String code, String name, int age) {
        super();
        this.code = code;
        this.name = name;
        this.age = age;
    }
}

public class Main {
    public static void main(String[] args) {
        MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee3 = employee1;
        System.out.println(employee1.equals(employee3));
        System.out.println("employee1.hashCode(): " + employee1.hashCode());
        System.out.println("employee3.hashCode(): " + employee3.hashCode());
        System.out.println(employee1.equals(employee2));
        System.out.println("employee2.hashCode(): " + employee2.hashCode());
    }
}

Вывод:

true
employee1.hashCode(): 511833308
employee3.hashCode(): 511833308
false
employee2.hashCode(): 1297685781

Поскольку employee3 указывает на тот же объект, что и employee1, вы получаете тот же хеш-код для них, а employee2 указывает на другой объект (хотя он имеет такое же содержимое, ключевое слово, new создаст отдельный объект в памяти), и поэтому вы можете редко получить тот же хеш-код для employee2, что и point # 4 упомянутое выше из документации гласит: As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects.

Вы должны переопределить метод hashCode так, чтобы он возвращал один и тот же хэш-код для двух объектов с одинаковым содержимым, например

class MyEmployee {
    String code;
    String name;
    int age;

    public MyEmployee(String code, String name, int age) {
        super();
        this.code = code;
        this.name = name;
        this.age = age;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((code == null) ? 0 : code.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }   
}

public class Main {
    public static void main(String[] args) {
        MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee3 = employee1;
        System.out.println(employee1.equals(employee3));
        System.out.println("employee1.hashCode(): " + employee1.hashCode());
        System.out.println("employee3.hashCode(): " + employee3.hashCode());
        System.out.println(employee1.equals(employee2));
        System.out.println("employee2.hashCode(): " + employee2.hashCode());
    }
}

Вывод:

true
employee1.hashCode(): 128107556
employee3.hashCode(): 128107556
false
employee2.hashCode(): 128107556

Реализация hashCode, приведенная выше, дает один и тот же хэш-код для employee1 и employee2, даже если equals возвращает false (отметьте как * 1048) * пункт № 3 , упомянутый выше из документа mentation).

Неправильный способ переопределения hashCode может привести к тому, что даже одни и те же объекты будут возвращать разные хеш-коды, например,

class MyEmployee {
    String code;
    String name;
    int age;

    public MyEmployee(String code, String name, int age) {
        super();
        this.code = code;
        this.name = name;
        this.age = age;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((code == null) ? 0 : (int) (code.length() * (Math.random() * 100)));
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee3 = employee1;
        System.out.println(employee1.equals(employee3));
        System.out.println("employee1.hashCode(): " + employee1.hashCode());
        System.out.println("employee1.hashCode() again: " + employee1.hashCode());
        System.out.println("employee3.hashCode(): " + employee3.hashCode());
        System.out.println(employee1.equals(employee2));
        System.out.println("employee2.hashCode(): " + employee2.hashCode());
    }
}

Вывод:

true
employee1.hashCode(): 66066760
employee1.hashCode() again: 66069457
employee3.hashCode(): 66073797
false
employee2.hashCode(): 66074882

Это неправильный способ переопределения hashCode, потому что вызов hashCode для одного и того же объекта более одного раза во время выполнения Java приложения должен последовательно возвращать одно и то же целое число (отметьте как point # 1 , упомянутых выше в документации).

Теперь рассмотрим следующий код и его вывод:

class MyEmployee {
    String code;
    String name;
    int age;

    public MyEmployee(String code, String name, int age) {
        super();
        this.code = code;
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        MyEmployee other = (MyEmployee) obj;
        if (code == null) {
            if (other.code != null)
                return false;
        } else if (!code.equals(other.code))
            return false;
        return true;
    }
}

public class Main {
    public static void main(String[] args) {
        MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee3 = employee1;
        System.out.println(employee1.equals(employee3));
        System.out.println("employee1.hashCode(): " + employee1.hashCode());
        System.out.println("employee3.hashCode(): " + employee3.hashCode());
        System.out.println(employee1.equals(employee2));
        System.out.println("employee2.hashCode(): " + employee2.hashCode());
    }
}

Вывод:

true
employee1.hashCode(): 511833308
employee3.hashCode(): 511833308
true
employee2.hashCode(): 1297685781

Поскольку employee1.equals(employee2) возвращает true, хэш-код также должен быть возвращен таким же (отметьте пункт # 2 , упомянутый выше в документации). Однако значения хеш-кода employee1 и employee2 отличаются, что не является правильным. Это различие заключается в том, что мы не переопределили метод hashCode. Таким образом, всякий раз, когда вы переопределяете equals, вы также должны корректно переопределять hashCode.

Наконец, ниже приведен правильный способ реализации hashCode и equals:

class MyEmployee {
    String code;
    String name;
    int age;

    public MyEmployee(String code, String name, int age) {
        super();
        this.code = code;
        this.name = name;
        this.age = age;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((code == null) ? 0 : code.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        MyEmployee other = (MyEmployee) obj;
        if (age != other.age)
            return false;
        if (code == null) {
            if (other.code != null)
                return false;
        } else if (!code.equals(other.code))
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}

public class Main {
    public static void main(String[] args) {
        MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
        MyEmployee employee3 = employee1;
        System.out.println(employee1.equals(employee3));
        System.out.println("employee1.hashCode(): " + employee1.hashCode());
        System.out.println("employee3.hashCode(): " + employee3.hashCode());
        System.out.println(employee1.equals(employee2));
        System.out.println("employee2.hashCode(): " + employee2.hashCode());
    }
}

Вывод:

true
employee1.hashCode(): 128107556
employee3.hashCode(): 128107556
true
employee2.hashCode(): 128107556
0 голосов
/ 14 марта 2020

Вам необходимо переопределить метод equals, в противном случае метод Object equals будет использоваться для сравнения двух экземпляров.

@Override
public boolean equals(Object that) {
    if (this == that) return true;
    if (that instanceof Elem) {
        Elem thatElem = (Elem) that;
        return thatElem.v == this.v;
    }
    return false;
}
...