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 failing 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 k开发者_Go百科eys
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));
}
}
You need to change
public boolean equals(ActorId other) {
....
}
to
public boolean equals(Object other) {
....
}
Tip of the day: Always use @Override
annotation.
If you had used the @Override
annotation, the compiler would have caught the error and said:
The method equals(ActorId) of type ActorId must override or implement a supertype method
Your code is correct, but you also need to override the equals
method inherited from Object
.
Add this to your ActorId
class:
@Override
public boolean equals(Object other) {
if(other == null || other.getClass() != getClass())
return false;
return equals((ActorId)other);
}
You definitely must override the method equals(Object), and for certain implementation of a Map (HashMap) it is also necesary that you overrdide the method hashCode().
I had the same problem, and without the custom hashCode implementation the equals method of the class "ActorId" was never called.
By default Java invokes boolean equals(Object obj);
So, you login is correct but if you want to OVERRIDE equals() use Object as a parameter and and check the class by instanceOf
or getClass()
and do a class casting.
if (obj instanceOf ActorId) {
ActorId other = (ActorId)obj;
... compare fields
}
精彩评论