Weird Set.contains () Behavior

I initially started this as a test for the best practice theory of the question I wanted to ask here, but in this process I found interesting behavior in the java.Set class. Initially, I wanted to know any potential errors of this approach, but now that I see that this does not work at all, I would like to know why.

I have some objects that are containers for database objects for my application. All objects have unique integer id , and hashCode() and equals() determined by integer identifiers (for storage in hash networks).

Well, I need the ability to check if the hashset contains the object specified only for the identifier. Of course, I could create a new instance of the object and test it. But, just for the kicks, I wanted to see if I could do it. Of course, this is also trivial with a hashmap, so this is really not an important issue, just for fun and knowledge.

So, I created a class and tried to call contains() on an integer instead of an object instance. Netbeans, of course, gives a funny warning for this.

 Suspicious call to java.util.Collection.contains: Given object cannot contain instances of int (expected Person) 

Ignoring the error and running the code, I was shocked to find that Java does not even call the equals method. I put the debugging System.out.println() in my equals method to check and yep, it is not even being called.

In the code below, the expected result should be (if my theory were correct ):

 Here Yes Here Yes 

or (if my theory was wrong ):

 Here Yes Here No 

However, the output is:

 Here Yes No 

Note that there is no "Here" before "No", proving that the equals method is not even called.

Can anyone shed some light? I was always told to add this to equals() for efficiency:

 if (!(obj instanceof Person)) return false; 

But if equals() is not even called in such a situation, then this would be pointless.

Here is the SSCCE:

Thank you for your time.

  import java.util.LinkedHashSet; import java.util.Set; /** * * @author Ryan */ public class Test7 { public static void main(String[] args) { class Person { public final int id; public final String name; public Person(int id, String name) { this.id = id; this.name = name; } @Override public boolean equals(Object obj) { System.out.println("Here"); if (this == obj) return true; if (obj instanceof Person) return id == ((Person)obj).id; else if(obj instanceof Integer) return id == (Integer)obj; else { System.out.println("Returning False"); return false; } } @Override public int hashCode() { return id; } } Set<Person> set = new LinkedHashSet<Person>(); set.add(new Person(1, "Bob")); set.add(new Person(2, "George")); set.add(new Person(3, "Sam")); if(set.contains(new Person(1, "Bob"))) System.out.println("Yes"); else System.out.println("No"); if(set.contains(1)) System.out.println("Yes"); else System.out.println("No"); } } 
+4
source share
1 answer

This is because the comparison is performed with the provided object , and not with the elements in the set. From HashSet # contains (object) :

Returns true if this set contains the specified element. More formally returns true if and only if this collection contains an element e such that (o == null? E == null: o.equals (e)) .

So, in your example you will compare, for example integer.equals(person) . Therefore, if your collection contains Person objects, the condition if(obj instanceof Integer) will never be checked, but if your collection contains Integer objects, this condition will be fulfilled and as such will be checked.

+5
source

Source: https://habr.com/ru/post/1484106/


All Articles