0

I have a MultiKey object as keys for a Map.

A Key consists of a Name (String) and an ID (int).

The following contract has to be fullfilled: Keys have to be equal if either the names of both keys are equal or the ids of both keys.

How do I have to implement the hashCode() function so that this contract is not violated? Is it even possible?

Implementing equals is easy... i just put:

if (name.equals(other.name) || id == other.id)
    return true;

But this won't work because hashMap only uses hashCode() and does not care about equals()...

Example:

Map A = [ ("tom",1)=TOMAS, ("eli",2)=ELIAS ]

A.get(new Key("tom",0))    should return TOMAS
A.get(new Key("",1))       should return TOMAS
A.get(new Key("eli",2))    should return ELIAS
...
Jan
  • 2,025
  • 17
  • 27
  • 4
    What about `A.get(new Key("tom",2))` ? – Tunaki Feb 27 '15 at 20:48
  • No, thats the point. Two keys have to be identical if either the names OR the ids are equal – Jan Feb 27 '15 at 20:48
  • 3
    I understood that but given your map `A`, what `A.get(new Key("tom",2))` should return ? – Tunaki Feb 27 '15 at 20:50
  • Oups... Damn there has to be an easy way to fullfill that contract in a clear and simple way. – Jan Feb 27 '15 at 20:50
  • Perhaps it may be easiest to have two separate maps, one keyed with a string and the other keyed with an int. – wilkesybear Feb 27 '15 at 20:52
  • I already did that but I wanted a generic graph class that stores values by keys... And I wanted it to be upward compatible for MultiKeys as well. – Jan Feb 27 '15 at 20:54
  • But MultiKeys implies multiple maps, not multiple keys within the same map....Just use multiple maps. – Shashank Feb 27 '15 at 20:57
  • No not necessarily, If I did not have to fullfill the contract that I can return a value by just knowing one of the internal key labels, then it would be perfectly fine. – Jan Feb 27 '15 at 20:58
  • Either the 'by bame' or the 'by id' will have precedence over the other. As your strange contract is, you'll always be searching by one key and using the other one to resolve when there's a draw on the first one. Moreover, your implementation of `equals()` violates it's contract because it's not transitive. – fps Feb 27 '15 at 21:19
  • The solution to this is obviously to not use a hashmap. Use a treemap instead, that way you can still make use of your equals method. Also I should point out that with the way you have set this up, it is possible for one item to belong to 2 buckets in the hashmap, so what do you do about this? Another solution if you decide to go with the hashing solution is to use only one key - either the name or the Id – smac89 Feb 27 '15 at 21:20

2 Answers2

1

About the only way I can see to do this would be to build a set to TreeSet to cache the hashCodes for the keys. Then use the first equals value encountered as the hashCode value for the current execution. Problems with this:
a. Can use a lot of extra memory if there are many distinct keys.
b. The hashCode values will not necessarily be consistent across multiple executions of the program.
c. If multi-threaded, synchronization will be required against the cached hashCodes.

If you do this, the hashCode could simply be generated combining name and id like you always would.

user3745362
  • 229
  • 1
  • 5
0

The worst case scenario: always return the same hashCode. The specs say in short: - if 2 objects are equal, they must have the same hashCode. If 2 objects are unequal, they can still have the same hashCode. A hashCode is there primarily for performance.

Robert Scholte
  • 11,889
  • 2
  • 35
  • 44