Another solution to the Java equals/compareTo Problem (More or Less) February 26, 2007 | 02:35 pm

I was talking to some of the people at my new contract here, and I realized I never posted this revelation.

There is an partial solution to that annoying Java equals/compareTo issue. After you know that the object that you are comparing to is apparently equal to you, you then need to return this.hashCode() == them.hashCode(). Assuming people have properly overridden hashCode, this should mostly keep you safe.

Of course, there is the unfortunate possibility that you could get a false positive here: the quality of your hashCode calculation will determine how likely that is. And the this.getClass().equals(them.getClass()) || them.equals(this) can have infinite recursion of two sibling classes are accidentally equal to eachother.

So I’m still struggling with this. Help?

Tags:

  • Brian

    Worse yet, the hash equals doesn’t work. Consider the case you brought up in your original post on this- comparing a 3D point to a 2D point. Assume that you want to compare them equals if x3D = x2D, y3D = y2D, and z3D = 0. Unfortunately, for any reasonable hash value, the hash of (x, y, 0) will be different than the hash of (x, y). This is also assuming that the two classes use the same hash function, but that’s a different problem.

    I am rapidly comming to the opinion that the best implementation is:

    public boolean equals (Object them) {
    throw Exception(“equals doesn’t work! Use a comparator!”);
    }

    However, this would likely not be popular in most Java shops.

  • Brian

    Another comment I’ll make- this problem shows up in C++ as well- the problem occurs wether the equality function is called equals or operator==. The difference is that in C++ there is no written contract that operator== has to abide by. For example, a == b does not necessarily have to imply b == a.

  • http://enfranchisedmind.com/blog Robert

    When I write hash values, I tend to associate each field with its own arbitrary binary value, which is then XORed with the natural hash of the calculation. The cute implementation is to define those at runtime using RandomUtils, and have that generator check to make sure we’re not getting duplicates — the more mundane version is to just generate a random number between MIN_INT and MAX_INT and hardcode that into the hash-code implementation, and for child classes to double-check the calculation.

  • http://enfranchisedmind.com/blog Robert

    Of course, the way I implement things hoses up the case where (x=a,y=b), (x=a,y=b,z=0) — there, you might actually *WANT* them to be equal. Or you might not want to assume that the 2D point sits on the z=0 plane. Who knows.

    And that’s really the rub, isn’t it?

  • http://enfranchisedmind.com/blog Robert

    And one last thought before I let this go. A quick save for this implementation would be to define the equals and compareTo method to only work with objects of the same type. I think this was the conceptual usage of equals and compareTo for the original Java layout, and all the issues are neatly sidestepped at that point.