views:

2711

answers:

3

I have the following code:

class IncidentTag:
     def __init__(self,tag):
        self.tag = tag
     def equals(self,obj):
        return self.tag.equals(obj.tag)
     def hashCode(self):
        return self.tag.hashCode()

from java.lang import String
from java.util import HashMap
from java.util import HashSet

tag1 = IncidentTag(String("email"))
tag1copy = IncidentTag(String("email"))
tag2 = IncidentTag(String("notemail"))

print tag1.equals(tag1copy)
print tag2.equals(tag2)

print "Now with HashSet:"

hSet = HashSet()
hSet.add(tag1)
hSet.add(tag2)

print hSet.contains(tag1)
print hSet.contains(tag2)
print hSet.contains(tag1copy)

The output is: 1 1 Now with HashSet: 1 1 0

However, I would have expected the last line to be true (1) as well. Is there something obvious that I am missing.

(yes, I know that my equals method and hashcode methods do not take some issues into account... they are deliberately simple, but do let me know if the issues there are causing this problem)

+1  A: 

I wrote equivalent code in Java and it does produce true for all three contains() calls. So I think this must be an oddity in Jython. Maybe the underlying Java objects are not exactly what you see them as in Python.

Dave Costa
I also wrote the same code in Java - and had the same results as Dave Costa.
matt b
+10  A: 

You shouldn't implemented the Java-Style equals and hashCode method, but instead the Python equivaltents __eq__ and __hash__. Adding

def __hash__(self):
    return self.hashCode()
def __eq__(self, o):
    return self.equals(o)

helps. These python methods are - as far as I know - dynamically bound to hashCode and equals() by Jython. This ensures that you can put Python classes into Java's collections.

Now the code prints five "1".

dmeister
That's just weird. I guess language bindings are like that, but I haven't seen this documented anywhere. Anyway, this does fix the problem... thanks!
jsight
A: 

I don't know Python, but it sure looks like the underlying Java object's equals() and hashcode() aren't honoring the required contract.

  • Two objects if equals() must return the same hashcode().

It looks like that is violated. HashSets are first going to use the hashcode in the lookup to get the list the matching object would be in, then go through the list to find the one that's equal. If your hashcode isn't honoring the contract and they're returning different hashcodes, then it won't find it in the hashset even if they were equals() comparable.

The default Java Object.hashcode() isn't going to return the same hashcode for 2 objects. You've got to override it.

Chris Kessel