7

For a class whose fields are solely primitive, ex.:

class Foo
{
    int a;
    String b;
    boolean c;
    long d;

    boolean equals(Object o)
    {
        if (this == o) return true;
        if (!(o instanceof Foo)) return false;
        Foo other = (Foo) o;
        return a == other.a && b.equals(other.b) && c == other.c && d = other.d;
    }
}

Is this a reasonably "good enough" way to write hashCode()?

boolean hashCode()
{
    return (b + a + c + d).hashCode();
}

That is, I construct a String out of the same fields that equals() uses, and then just use String#hashCode().

Edit: I've updated my question to include a long field. How should a long be handled in hashCode()? Just let it overflow int?

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • While not terribly efficient, it should work just fine because any two instances with all the same internal values will have the same hashcode. – Gabe Dec 14 '10 at 17:32
  • It will work correctly i dont know about the performance issue about it but it certainly will work. If you wanna go further then read: http://www.ibm.com/developerworks/java/library/j-jtp05273.html – Necronet Dec 14 '10 at 17:35
  • Just use [commons-lang's HashCodeBuilder](http://commons.apache.org/lang/api-2.5/org/apache/commons/lang/builder/HashCodeBuilder.html) and never have to worry this type of thing – matt b Dec 14 '10 at 17:35
  • @Matt Ball: the presence of *equals* and *hashCode* at the top of the Java OO hierarchy is a major SNAFU that cannot be understated. *equals* and *hashCode* are incompatible with mutability, for a start. So you may think it's ok if you do *"OO over immutable objects"* but then here comes the second gotcha: *equals* and *hashCode* are incompatible with inheritance. Don't take my word of it, here's what the great Joshua Bloch (and many others) says on the subject: **There's simply no way to extend an instantiable class while preserving the equals contract** (from *Effective Java*). – SyntaxT3rr0r Dec 14 '10 at 18:20
  • 1
    @Spoon: thanks for the comment, though I don't think it's wholly relevant to the question at hand – Matt Ball Dec 14 '10 at 18:23
  • @Matt Ball: In addition to the fact that it's impossible to do both OO and use *equals/hashCode* there are multi-threading issues to be considered too (for example your *equals* method doesn't look to be synchronized on anything, which will only work if you do OO over immutable objects [but, granted, *equals* and *hashCode* are totally broken anyway if you work with mutable objects]). In short, *equals* and *hashCode* are broken but for the simplest use case: no inheritance, no mutability and you work carefully with simple collections from the API. – SyntaxT3rr0r Dec 14 '10 at 18:24
  • @Matt Ball: it's not relevant indeed if you use Java purely in a procedural way, without any kind of inheritance, without any multi-threading and by making sure you're only using immutables objects. Which you certainly all do right !? ;) Glad I could help a bit ;) – SyntaxT3rr0r Dec 14 '10 at 18:26
  • @Spoon: since you mention it, I am actually writing a class which extends `Object` and is `final` (so, cannot be extended), and whose instances are immutable. Problem solved. Works in a multi-threaded program. – Matt Ball Dec 14 '10 at 18:31
  • 1
    @SpoonBender: You have a good point, but since Matt is just a consumer, and not involved with the Java specs, it really doesn't matter in the context of this question, unless it would somehow change the "correct" answer to his question. Should he program his Java code differently somehow based on the knowledge that HashCode shouldn't have been associated with the Object class? – StriplingWarrior Dec 14 '10 at 18:50
  • @SyntaxT3rr0r: There is nothing wrong with having a means of asking each and every object whether it is equivalent to the object pointed to by a reference. Every kind of object, be it mutable or not, can answer the question, no matter what kind of thing the reference points to. If an object is mutable, it is only equivalent to the target of the reference if it *is* the target of the reference. Building a list of distinct objects is often useful, and the default `equals` and `hashCode` implementations are just fine. The problem in Java is that many classes... – supercat Jan 13 '13 at 17:32
  • ...define those methods to mean something other than equivalence, which in turn is a result of an inability to specify that classes like `Hashmap` should use some other definition of equivalence (such other definitions benefitting from knowledge of the particular things--not just types but instances--being compared). – supercat Jan 13 '13 at 17:37

2 Answers2

7

Your hash code does satisfy the property that if two objects are equal, then their hash codes need to be equal. So, in that way it is 'good enough'. However, it is fairly simple to create collisions in the hash codes which will degrade the performance of hash based data structures.

I would implement it slightly differently though:

public int hashCode() {
    return a * 13 + b.hashCode() * 23 + (c? 31: 7);
}

You should check out the documentation for the hashCode() method of Object. It lays out the things that the hash code must satisfy.

jjnguy
  • 136,852
  • 53
  • 295
  • 323
  • I understand the contract for `equals()` and `hashCode()`. What I'm looking for is a minimal-effort, idiot-resistant,no-surprises way to fulfill the contract. Your code seems reasonable enough. Could you explain the reason for multiplying by prime numbers? Also, I have updated my question: how do I deal with `long` fields? – Matt Ball Dec 14 '10 at 17:55
  • 3
    @Matt - The "minimal effort, "idiot resistant" path is use your IDE to generate the method. E.g. Netbeans generates for one of my classes: `int hash = 5; hash = 37 * hash + (int) (this.id ^ (this.id >>> 32)); return hash;` – Amy B Dec 14 '10 at 18:14
  • 1
    @Coronatus: hmm, good point. I forgot about `Alt + Shift + S` (Eclipse) – Matt Ball Dec 14 '10 at 18:17
  • @Matt, the reason is to give numbers in different positions different values. Example: your object has `int a`, and `int b`. If you simply add the two numbers, the hash codes will collide int the case of (a=5, b=2 and a=2, b=5). If you multiply `a` by a number and `b` by a different number. Those cases no longer collide. – jjnguy Dec 14 '10 at 18:22
  • @Matt, because... (I have no idea. That is just the convention that I have seen used everywhere else.) – jjnguy Dec 14 '10 at 18:29
  • 1
    @Matt, maybe this will help. http://stackoverflow.com/questions/3613102/why-use-a-prime-number-in-hashcode – jjnguy Dec 14 '10 at 18:32
0

It totally depends on what your data will look like. Under most circumstances, this would be a good approach. If you'll often have b end with a number, then you'll get some duplicate codes for unequal objects, as JacobM's answer shows. If you know ahead of time that b will pretty much never have a number value at the end, then this is a reasonable hashing algorithm.

StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315