-5

As I understand, == operator is used for reference comparison, i.e. whether both objects point to the same memory location or not. To test this I created a small java program, but getting not consistent results. Do anyone have any clue on this?

import java.util.HashSet;
import java.util.Set;

public class EqualOperatorTest {
    public static void main(String[] args) {
        boolean result;
        Employee employee1 = new Employee();
        employee1.setId(1);
        employee1.setName("XYZ");

        Employee employee2 = new Employee();
        employee2.setId(1);
        employee2.setName("XYZ");

        result = employee1.equals(employee2);
        System.out.println("Comparing two different Objects with equals() method: " + result);  //1. This returns True. Ok as we have overridden equals

        result = (employee1 == employee2);
        System.out.println("Comparing two different Objects with == operator: " + result);   //2. This returns False. Why? hashcode of both employee1 and employee2 are same but still == comparison returning false.

        //3. Validating hashcode of reference variables employee1 and employee2. We can see both has same hashcode (bcos we have overridden hashcode too in Employee class). But then why above employee1 == employee2 is returning false. Afterall == compares the memory location (or hashcode) and both have the same hashcodes,
        System.out.println("employee1 : " + employee1);   //employee1 and employee2 has same hashcode   
        System.out.println("employee2 : " + employee2);   //employee1 and employee2 has same hashcode   

        // Creating hashset and storing the same employee references.
        Set empSet = new HashSet();
        empSet.add(employee1);
        empSet.add(employee2);

        System.out.println(empSet);   //returns only one memory location(hashcode) as we have overridden hashcode in Employee class. Ok

        employee1 = employee2;      //Setting employee2 in employee1
        result = (employee1 == employee2);
        System.out.println("Comparing two reference pointing to same Object with == operator: " + result);  //This returns True. Ok 

    }
}

Need to understand why behavior of == operator is not same at 2 and 3.

My Employee class is simple with two class level variables, their getters and setters and overridden equals and hashcode methods

public class Employee {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        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;
        Employee other = (Employee) obj;
        if (id != other.id)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }
}
vinsinraw
  • 2,003
  • 1
  • 16
  • 18
  • 3
    `==` compares *references* - it's asking whether the two references refer to the exact same object. That's not the same as comparing *equal* objects. Two objects can be "not the same object" but equal. I suggest you read https://stackoverflow.com/questions/32010172 which will hopefully clarify things for you. – Jon Skeet Jul 02 '17 at 09:04
  • Also note that `hashCode()` is only the *first* part of checking equality - a `HashSet` checks `hashCode()` and then `equals`, as two non-equal objects can still have the same hash code. – Jon Skeet Jul 02 '17 at 09:04
  • 7
    `As I understand, == operator is used for reference comparison, i.e. whether both objects point to the same memory location or not` - if you understand that, why do you expect that two different objects (even if they have the same `hashCode`) would return true when applying == on them? – Eran Jul 02 '17 at 09:05
  • 2
    *== compares the memory location (or hashcode)*: the hashCode and the memory location are unrelated. Do your really think that by making all hashCode() methods return the same value, all the objects would end up being stored in the same location in memory? That doesn't make sense, right? – JB Nizet Jul 02 '17 at 09:09
  • @Jon My query was on the same. Why empoyee1==employee2 is false (at 2) even though both employee1 and employee2 has same hashcodes (as evident by 3). == compares hashcodes of two object references or compares something else. Thats my curosity... – vinsinraw Jul 02 '17 at 09:35
  • @JB thanks for the clarification on hashcode and memory location. So it means no two objects can have same memory address at the same time even though they may have same hashcodes. – vinsinraw Jul 02 '17 at 09:53
  • Yes, of course. – JB Nizet Jul 02 '17 at 09:54
  • 2
    *"== compares hashcodes of two object references"* ... I don't get it in my head how you can write the correct behaviour of == in the first line of the question and then completely ignore that and assume something else. – Tom Jul 02 '17 at 09:55
  • @Tom. I had the understanding that == compares memory location and had the impression that memory location is same as hashcode of the object. Actually each object has its own memory address and is different from their hashcodes. With that said, even though two objects can be equal as per their state (the object's data), and can have same hashcodes (as was in my example) but internally their memory addresses will still be different. And this explains the behavior of == returning false as it was comparing memory address. – vinsinraw Jul 02 '17 at 10:56

2 Answers2

1

You are right about the fact that == compares the references of the two variables, but, references are not the same as hash codes.

Hash codes are numbers returned by this method that you've written:

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

While references are memory locations at which the objects are stored. They are completely different.

It is understandable that you have this misconception. If you did not override hashCode, it would have returned the memory location of the object, so the hash code would have been the same as the memory location. However, since you have overridden hashCode, they are not the same any more.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • *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 Java™ programming language.)* https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode-- – JB Nizet Jul 02 '17 at 09:19
  • Thanks for the clarifications and yes understood the difference between hashcode and memory addresses among objects. Also got some additional tips @ https://stackoverflow.com/questions/25539351/if-two-objects-have-same-hashcode-then-what-about-their-memory-addresses I think this explains the behavior at 2 and 3. – vinsinraw Jul 02 '17 at 09:55
  • @vinsinraw If you think my answer answers your question, please consider accepting it by clicking on that checkmark! – Sweeper Jul 02 '17 at 10:30
0

Operator == checks if two references points to the same object in memory (for primitive-types like int or double operator == checks values for equality).

Employee a = new Employee(...); 
Employee b = new Employee(...);

System.out.println(a == b); // false
System.out.println(b == a); // false
System.out.println(a == a); // true
System.out.println(b == a); // true

Employee c = b;
System.out.println(a == c); // the same as a == b
System.out.println(b == c); // the same as b == b

Object#equals(Object) checks whether content of two objects is the same.

Employee a = new Employee(1, "XYZ");
Employee b = new Employee(1, "XYZ");

// if Employee#equals(Object); is overridden so that, 
// it checks both id and name for equality 
// then we can expect that all these lines returns true
System.out.println(a.equals(b)); // true
System.out.println(b.equals(a)); // true
System.out.println(a.equals(a)); // true
System.out.println(b.equals(b)); // true

Employee c = b;
System.out.println(a.equals(c)); // this is the same as a.equlas(b);

Employee d = new Employee(2, "ABC");
System.out.println(a.equals(d)); // false 

Object#hashCode() returns integer number that identifies content (if two objects are equal, then hashcodes have to be the same).

Employee a = new Employee(1, "XYZ");
Employee b = new Employee(1, "XYZ");

System.out.println(a.hashCode() == b.hashCode());  // true expected since a and b are equal

Employee c = b;
System.out.println(a.hashCode() == c.hashCode()); // the same as a.hashCode() == b.hashCode()

Employee d = new Employee(1, "xyz");
// might be false, but can be true, 
// it all depends on quality of hashCode() implementation
System.out.println(a.hashCode() == d.hashCode()); 

Hashcodes are used in hash-based datastructures to find desination index of the object i. e. index where to store the object. Suppose Employee#hashCode() returns 20 for 1, "XYZ". If hashmap's underlying array has size 11, then destination index will be 20 modulo 11; (9). So when you invoke hashSet.contains(new Employee(1, "XYZ")); it will look at index 9. Note, perfect hash-function which returns always unique number for given values does not exists that is, it can happen that your hashCode() implementation may return number 20 also for attributes values: "1, "xyz" (lower-case) which results to the same hash-index 9. Thus after hashset has found object at index 9, it have to perform also equality check to ensure, that object stored at index 9 is equal to object that is passed in contains(Object).

matoni
  • 2,479
  • 21
  • 39