0

Let's say I have the following class A that implements an interface M:

class A implements M {
   int i;

   int hashCode() { return i; }
}

And a wrapper for A, for example

class AWrapper implements M {

    A a;

    public Wrapper(A a) {
       this.a = a;
    }

    int hashCode() { ??? }
}

This hierarchy is a sort of Composite Pattern. My question is what is a good hashcode for the AWrapper class? I can use a.hashCode(), but then all A's and AWrapper's will have the same hashcode. What is the best way to implement such a hash code?

As some people ask what's the rationale behind this design, let me make it more concrete. I'm writing a data type for regular expressions. So A is a symbol, and there are regular operators such as *, +, ? that are essentially wrappers of symbols, e.g., Star(A). I also want to make sure there is only one instance of an object, that's why I need sharing, so if someone writes:

r1 = a*a
r2 = a*c

represented by Seq(Star('a'), 'a') and Seq(Star('a'), 'c'), I want two instances of Star('a') be shared, of course via a factory call.

Wickoo
  • 6,745
  • 5
  • 32
  • 45
  • Is there any reason why the wrapper shouldn't have the same hashcode? – pvg Dec 07 '15 at 18:52
  • They will be put into the same HashMap, so I want to avoid collisions. – Wickoo Dec 07 '15 at 18:52
  • Out of curiosity, what is the scenario in which you need to differentiate between `A` and `AWrapper`? If they have the same hash code, I don't see the reason to provide a wrapper class – James Wierzba Dec 07 '15 at 19:04
  • @JamesWierzba see the update – Wickoo Dec 07 '15 at 19:04
  • If you're going to be calling a factory for these, you don't need to bother with hashCode. Your factory can just track the symbol->wrapper mapping itself. – pvg Dec 07 '15 at 19:08
  • @pvg I don't fully understand your comment, could you elaborate? My factory is essentially a Map that takes an instance at creation time and returns an already existing one or creates a new one. It is sort of for sharing. – Wickoo Dec 07 '15 at 19:11
  • @Wickoo I guess I don't understand why your map isn't string->objectyouwantointern, instead, based on your example above. – pvg Dec 07 '15 at 19:13
  • @pvg that's a possibility to use toString and in this case to string will be unique based on hierarchy, but was looking for a way not to that. – Wickoo Dec 07 '15 at 19:17
  • @Wickoo overriding hashcode signs you up for a rather stringent contract - if you haven't yet, take a look at http://stackoverflow.com/questions/27581/what-issues-should-be-considered-when-overriding-equals-and-hashcode-in-java or many similar. If all you want to do is make sure a bunch of instances are interned, you probably don't need to override hashcode and all it entails. You can simply keep a map of the unique state of the instances to the instances themselves. – pvg Dec 07 '15 at 19:23
  • @pvg thanks, will take a look. so you are proposing to have another object that represents unique state of instances? – Wickoo Dec 07 '15 at 19:34
  • @Wickoo what I mean is if you have Factory.instantiate(Param) -> Result, then there's nothing stopping factory from just keeping a map of Param to Result. More generally, though you should just review your design and then write up a question about the design options you're considering. What you have now is a very specific implementation question that's hard to get good answers for because the context is pretty vague. – pvg Dec 07 '15 at 19:43
  • @pvg I don't see how a map from param to result will be anyhow different. You have to implement the hashmap somehow. My question is clear, and I agree with you that it's hard to give a good answer, because it's just hard to give a different hashcode without extra structural elements, but what is interesting is that people want to know why I need to do that, instead of saying how to do it. – Wickoo Dec 07 '15 at 20:03
  • @Wickoo if the param, is, say, String like in your example above, you don't need to touch the hashcode of Seq. It's actually trivial to give a different hashCode without extra elements - you can not override hashcode. You can flip the bits, etc. People are asking why because context matters. For instance, you haven't mentioned equals at all which is essential if you're going to override hashcode. – pvg Dec 07 '15 at 20:16

1 Answers1

1

Putting As and AWrappers as keys in the same map does not sound right. But if that's what you want you could use Objects.hash(a).

When a single object reference is supplied, the returned value does not equal the hash code of that object reference.


As a side note, if AWrapper is immutable and the hashCode method is called often and A::hashCode is not trivial and memory footprint is not an issue, you may want to precalculate the hashcode (worth testing if it is worth it in your scenario):

class AWrapper implements M {

    private final A a;
    private final int hash;

    public Wrapper(A a) {
       this.a = a;
       hash = Objects.hash(a);
    }

    int hashCode() { return hash; }
}
assylias
  • 321,522
  • 82
  • 660
  • 783