0

I have a HashSet<Obj> containing one item. A new item trying to be added into the Set is the same as the one existing item, .equals(). To confirm newElement is in fact the same, I have some debug prints looping through my HashSet and printing for each item:

does current item .equals(newElement).

This confirms there is a .equals() object in the set already.

This is where the fun starts, if I call add(newElement) I am expecting it to not add, or at least overwrite what's already in the set. The set should only have the 1 unique item after the add. In my case, it has 2!

To help figure out why add() was working that way, I ran a Set.contains(newElement) which should have returned true, but in my case it returns false. This is why my add() works the way it does.

Any reason why an item in a set could be .equals(newElement) but Set.contains(newElement) could return false? I have checked my .equals() and it seems to work the way I expect, printing out the objects show what .equals() is confirming. I thought maybe something with how HashSet handles add and contains but that checks (o==null ? e==null : o.equals(e)) from the Java documentation.

I am also overriding hashCode(), the values used within I am printing as part of my debug which shows the same logical items.

Michael Berry
  • 70,193
  • 21
  • 157
  • 216
Walls
  • 3,972
  • 6
  • 37
  • 52
  • 1
    What about `obj.hashCode()`? Is that equal for the existing and new objects? – Andy Turner Dec 20 '17 at 14:59
  • I am printing out logically whats in hash code, I will def. do a loop explicitly checking `.hashCode` to check there. – Walls Dec 20 '17 at 15:01
  • 5
    How about showing us your code, rather than trying to describe it? – Andy Turner Dec 20 '17 at 15:01
  • @AndyTurner & Jesper I can show code (and will) but it'll take a little time to obfuscate it. I am adding in hashcode prints now to check those out. The only thing I can think of is the hashcode is ~= which is why contains is failing. Will report back after testing :) – Walls Dec 20 '17 at 15:06
  • 1
    "The only thing I can think of is the hashcode is ~= which is why contains is failing" Yes, that is the only reason, if `equals` is returning true. – Andy Turner Dec 20 '17 at 15:06
  • @AndyTurner I'll dupe hammer myself closing... – Walls Dec 20 '17 at 15:23
  • 1
    Possible duplicate of [How to ensure hashCode() is consistent with equals()?](https://stackoverflow.com/questions/410236/how-to-ensure-hashcode-is-consistent-with-equals) – Walls Dec 20 '17 at 15:24

2 Answers2

3

Any reason why an item in a set could be .equals(newElement) but Set.contains(newElement) could return false?

Yes - you need to implement hashCode() as well as equals, and it needs to check exactly the same fields as equals(). You say hashcode is only approximately equal, which doesn't make much sense. If hashCode() returns a different result for two different objects (which it will by default, if you haven't overridden it), then the HashSet will assume them unique (even if equals() returns true.)

If hashCode() returns the same value for both objects, and equals() return true (symmetrically on both objects), then that will ensure you can't have both objects in the HashSet. There's no (sensible) exceptions to this rule, so if you think both hashCode() and equals() are behaving correctly and consistently, there must be a flaw in your logic somewhere.

Michael Berry
  • 70,193
  • 21
  • 157
  • 216
0

Equals and hashCode methods have a specific contract:

1.If the elements are equal to each other, i.e. equals returns true, then the hashCode value for these objects must match.
2.If the value of hashCode for objects is the same, then this does not mean that equals for them will return true, ie. Objects do not have to be equal to each other, i.e. collisions are possible.

Now consider each case separately:


1.Equals and hashCode are not overridden, this means that equals will return true only if the links are equal, and hashCode can be either equal or not. The size, regardless of the value of hashCode, will be 2.
2.equals and hashCode are redefined, then we will have the same hash value, we will get to the same table cell, equals will determine that the object in the list is already present, accordingly the size will be equal to 1.
3.equals is not re-defined, and hashCode is overridden. In this case, the cell index will be the same, but the same element will not be found in the list and therefore the size will be equal to 2.
4.equals is overridden, and hashCode is undefined. This depends on how the value for hashCode is generated in the Object class. If the values ​​are the same, then the list will be the same, and accordingly, the number of elements in the table will be 1. If different, the search will occur in different lists, and duplicates will not be found, then the size will be equal to 2.


Similarly, the remove, contains operation is performed.
For Example about HashSet/contains:

class Dog{
String color;

public Dog(String s){
    color = s;
}   

}

 public class SetAndHashCode {
    public static void main(String[] args) {
    HashSet<Dog> dogSet = new HashSet<Dog>();
    dogSet.add(new Dog("white"));
    dogSet.add(new Dog("white"));

    System.out.println("We have " + dogSet.size() + " white dogs!");

    if(dogSet.contains(new Dog("white"))){
        System.out.println("We have a white dog!");
    }else{
        System.out.println("No white dog!");
    }   
}

}