1

I'm doing a school assignment where I need to store a bunch of contacts. Each contact has a HashSet of social network accounts, addresses and phone numbers.

I DO NOT want to store multiple copies of the same object in my HashSet, so I have overridden the hashCode() and equals() method for each object.

But for my social network account object only,my HashSet is storing the same object twice! I have two different objects:

SocialNetworkAccount s1 = new SocialNetworkAccount(SocialNetworkAccount.AccountType.FACEBOOK, "wallcrawler123");

SocialNetworkAccount s2 = new SocialNetworkAccount(SocialNetworkAccount.AccountType.FACEBOOK, "wallcrawler123");

s1.equals(s2) returns true and s1.hashCode() == s2.hashCode() returns true, but s1 == s2 returns false! Why?

This is the hashCode() method I'm using:

public int hashCode() {
    int prime = 31;
    int result = 1;
    result = result*prime + type.hashCode();
    result = result*prime + id.hashCode();
    return result;
}
Ry-
  • 218,210
  • 55
  • 464
  • 476
hulky.smash
  • 23
  • 1
  • 4
  • 3
    Please post your `equals` method. – rgettman Nov 01 '13 at 23:21
  • 1
    If the hashes are equal and `equals` considers the two objects equal, the HashSet should keep only one of the two. Please post a [SSCCE](http://sscce.org/). –  Nov 01 '13 at 23:23
  • 1
    Your title and post are not in sync, I think. – Bhesh Gurung Nov 01 '13 at 23:24
  • @BheshGurung While the post asks why `s1 == s2` returns false, it also says "my HashSet is storing the same object twice". So at most the post contains more than the title, but that shouldn't be surprising ;-) –  Nov 01 '13 at 23:28
  • 3
    My guess / prediction: he created an equals() but with the wrong signature. The argument has to be Object, not SocialNetworkAccount. – user949300 Nov 01 '13 at 23:33
  • 2
    Given that they claim `s1.equals(s2)` and s1 and s2 have the same hashcode, I'm guessing that they don't realize `s1 == s2` is not equivalent to `s1.equals(s2)` – Jesan Fafon Nov 01 '13 at 23:39

4 Answers4

2

The == operator compares references. Since there are two different objects, their references will be different.

SocialNetworkAccount s1 = new SocialNetworkAccount(SocialNetworkAccount.AccountType.FACEBOOK, "wallcrawler123");

SocialNetworkAccount s2 = s1;

if (s1 == s2) { 
    System.out.println("The references are the same."); 
}
JustinDanielson
  • 3,155
  • 1
  • 19
  • 26
  • 1
    True, but this doesn't explain why `HashSet` stores both of them. It will call `equals` in this case. – rgettman Nov 01 '13 at 23:40
2

I suspect that the problem is that your equals method is not overriding Object's equals method, which takes an Object as a parameter.

I suspect that your equals method takes a parameter of SocialNetworkAccount, not Object. I can't know for sure because you haven't posted your equals method, but it is a possible explanation. In this case, the equals method in Object is not overridden, so when HashSet (eventually) calls it, it calls Object's equals method, which compares references to see if they are the same.

If so, modify your equals method to accept an Object instead of a SocialNetworkAccount, so it overrides equals properly.

Also, using the @Override annotation will catch when a method that intends to override another method in fact does not override it.

rgettman
  • 176,041
  • 30
  • 275
  • 357
1

hashCode method is used to coarsly assign entries to the table, but the equals(...) method has to return true for the two instances to be treated as identical.

Since you don't show us the equals method I assume you have not created your own version, which means the HashTable is useing the default implementation, which does a == b and your two instances are not the same instance, so that returns false, hence two entries in the table.

What you need to do is implement the equals method too:

public boolean equals(Object o) {
    if (o == this) {
        return true;
    }
    if (o instanceof SocialNetworkAccount) {
        SocialNetworkAccount their = (SocialNetworkAccount)o;
        return xxx.equals(their.xxx ) &&  yyy.equals(their.yyy) && ...;
    }
    return false;
}

Replace xxx and yyy and so on with the values in your class.

See the hashCode and equals contract

Community
  • 1
  • 1
rolfl
  • 17,539
  • 7
  • 42
  • 76
  • -1 The question states that equals is overriden (second paragraph) and that `s1.equals(s2)` returns true (while `s1 != s2`). –  Nov 01 '13 at 23:25
  • Yes it does, which I missed, but that's interesting .... I wonder if there's an inheritance hierarchy.... – rolfl Nov 01 '13 at 23:26
  • @delnan The question asserts that, without proof. It seems very likely the OP has done it incorrectly. This answer shows the correct way to do it. +1 – user207421 Nov 02 '13 at 00:17
1

maybe you have done somthing like this?

SocialNetworkAccount s1 = new SocialNetworkAccount(SocialNetworkAccount.AccountType.FACEBOOK, "foo");
SocialNetworkAccount s2 = new SocialNetworkAccount(SocialNetworkAccount.AccountType.FACEBOOK, "bar");

hashSet.add(s1);
hashSet.add(s2);

s2.setNickname("foo");

System.out.println(s1.equals(s2));  // true
System.out.println(hashSet);        // [FACEBOOK:foo, FACEBOOK:foo]
Michele Mariotti
  • 7,372
  • 5
  • 41
  • 73