4

I have a class that will be used in a HashSet. It only contains two members, and both are of the same type interface. This is what it looks like:

class MyClass{
    MyInterface a;
    MyInterface b;

    public int hashCode(){
         return a.hashCode() + b.hashCode();
    }

    public boolean equals(Object obj){
         if(!(obj instanceof MyClass)
              return false;
         MyClass other (MyClass) obj;
         return (this.a == other.a && this.b == other.b) || (this.a == other.b && this.b == other.a);
    }
}

As you can see, two instances of MyClass are "equal" if they contain the same two instances of MyInterface.

Now, I was thinking that for hashCode(), I could just add up the default hashcodes of its members. Is this good enough? If not, what is a proper implementation of hashCode() for this case?

10
  • how is a and b's hashcode defined ? Commented Jun 11, 2015 at 19:19
  • 1
    This will provide an unequal hashcode distribution: elements around Integer.MaxValue / 2 will be disproportionally represented compared to those at the extremes, leading to more hash collisions. Commented Jun 11, 2015 at 19:19
  • 2
    You could also use: return Objects.hash(a, b); Commented Jun 11, 2015 at 19:24
  • 2
    @assylias no, that would give two different hashcodes for equal objects, because [a, b] is equal to [b, a] Commented Jun 11, 2015 at 19:26
  • 2
    @assylias the OP appears to require something order-agnostic which Objects.hash is not Commented Jun 11, 2015 at 19:26

3 Answers 3

3

Yes, this is perfectly fine. It's equivalent to the implementation of Set.hashCode() for two-element sets.

Sign up to request clarification or add additional context in comments.

2 Comments

@River: Like I commented elsewhere, assuming a.hashCode() and b.hashCode() have proper hash code implementations that offer proper value distribution to begin with, I really don't see how the extra calculation will help reduce the likelihood of collisions. OP's implementation is fine.
That being said, you could almost certainly do better (less-collisions) with a finer-tuned hash function. The real question is whether it's worth your time to optimize this function rather than simply stick with one that is "good enough".
1

I would say no. Wouldn't this mean that these two instances of MyClass would hash to the same value:

MyClass {
  a.hashCode = 2;
  b.hashCode = 3;
}

and

MyClass {
  a.hashCode = 1;
  b.hashCode = 4;
}

6 Comments

So you have a hash collision. So what? Do you have a better suggestion?
This is bound to happen... Perfect hash functions are near impossible
Maybe my example is overly simplistic, but it seems like a more complete implementation could minimize the number of collision. Do I have a better suggestion? No.
You could do a combination of + and *, which are both associative and commutative (which are required): (a.hashCode() + b.hashCode()) * 31 + (a.hashCode() * b.hashCode()).
@Clashsoft: Assuming a.hashCode() and b.hashCode() have proper hashcode implementations that offer proper value distribution to begin with, I don't see any compelling reason to complicate the calculation.
|
0

Yes it is.

Because even with hashCode collision, the docs state:

It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results.

Comments