Why are custom objects not equivalent keys for a HashMap?

I'm having trouble using my own class as a key for a HashMap

public class ActorId { private final int playerId; private final int id; ActorId(int playerId, int id) { this.playerId = playerId; this.id = id; } public boolean equals(ActorId other) { return this.id == other.id && this.playerId == other.playerId; } public int hashCode() { int hash = 1; hash = hash * 31 + playerId; hash = hash * 31 + id; return hash; } public String toString() { return "#" + playerId + "." + id; } public int getPlayerId() { return playerId; } } 

Here is a failed JUnit test

  import static org.junit.Assert.*; import java.util.Map; import org.junit.Test; public class ActorIdTest { @Test public final void testAsMapKey() { ActorId a = new ActorId(123, 345); ActorId b = new ActorId(123, 345); assertTrue(a.equals(b)); assertEquals(a.hashCode(), b.hashCode()); // Works with strings as keys Map<String, String> map1 = new java.util.HashMap<String, String>(); map1.put(a.toString(), "test"); assertEquals("test", map1.get(a.toString())); assertEquals("test", map1.get(b.toString())); assertEquals(1, map1.size()); // But not with ActorIds Map<ActorId, String> map2 = new java.util.HashMap<ActorId, String>(); map2.put(a, "test"); assertEquals("test", map2.get(a)); assertEquals("test", map2.get(b)); // FAILS here assertEquals(1, map2.size()); map2.put(b, "test2"); assertEquals(1, map2.size()); assertEquals("test2", map2.get(a)); assertEquals("test2", map2.get(b)); } } 
+7
source share
4 answers

You need to change

 public boolean equals(ActorId other) { .... } 

to

 public boolean equals(Object other) { .... } 

Tip of the day: Always use the @Override annotation.

If you used the @Override annotation, the compiler would catch the error and say:

A method equal to (ActorId) of type ActorId must override or implement a supertype method

+9
source

Your code is correct, but you also need to override the equals method inherited from Object .

Add this to the ActorId class:

 @Override public boolean equals(Object other) { if(other == null || other.getClass() != getClass()) return false; return equals((ActorId)other); } 
+3
source

You definitely have to override the equals (Object) method, and for some Map (HashMap) implementation, you also need to redefine the hashCode () method.

I had the same problem, and without a custom hashCode implementation, the equals method of the "ActorId" class was never called.

+1
source

By default, Java calls boolean equals(Object obj); So, you are logging in correctly, but if you want OVERRIDE equals () to use Object as a parameter and check the class for instanceOf or getClass() and execute the class.

 if (obj instanceOf ActorId) { ActorId other = (ActorId)obj; ... compare fields } 
0
source

All Articles