Is there any way to create a class that gives identical references to class objects with the same valued variables, or otherwise any way to induce behaviour where I can easily track objects with the same variables? To elaborate, I am trying to implement the following code:
A Position class:
public final class Position {
private final int x, y, layer;
...
with relevant functions, and I am trying to track Entities with a
private Map<Position, List<Entity>> positionMap;
in another class. The problem is that to my understanding, once I stick a Position object as a key, I cannot create another Position object at the same "coordinates" and access the value held at the previous Position. Is there any easy workaround or else some kind of commonly accepted solution to this kind of problem? At the moment, my solution is to loop through each Position held in the keys and compare with .equals(), which defeats the point of a Map.
CodePudding user response:
I think you misunderstand.
Here, trivially:
public final class Position {
private final int x, y, layer;
// very important!
public int hashCode() {
// proper impl that mixes all 3 variables into it
}
public boolean equals(Object other) {
// proper impl
}
}
Then:
Map<Position, List<Entity>> map = new HashMap<>();
Position p1 = new Position(1, 2, 5);
Position p2 = new Position(1, 2, 5);
System.out.println(p1 == p2); // false, as expected.
map.put(p1, List.of());
System.out.println(map.get(p2)); // prints an empty list, NOT null!
map.put(p2, List.of());
System.out.println(map.size()); // 1, not 2!
The map impl already loops through all keys and compares them with equals
- you don't also have to do that. The reason HashMap is much faster than literally doing that is because it also uses hashCode()
: The rules of the hashCode
method state that any 2 objects whose hashCode are different cannot be identical (note that the reverse is not true; 2 objects with identical hashCode need not be equal). Hence, hashmap first calls hashCode to reduce the search space to a tiny bucket compared to the size of the whole map, and needs to do a full loop-scan through just the keys in this bucket. Usually a bucket contains just 1 object so that's great.
hashCode is not a key - This works:
public final class Position {
private final int x, y, layer;
// very important!
public int hashCode() {
return 1; // this is a really bad impl
}
public boolean equals(Object other) {
// proper impl
}
}
You can stuff the above Position objects in a hashmap if you must, and it'll work correctly. It won't be very fast - because all position objects have the same hashCode, it's 100% collisions, so the hashmap needs to loop through all keys for all operations. But it'll work. In the same vein, given a hashCode of position, you cannot do a lookup. HashMap simply doesn't allow that, and it wouldn't know what to return even if it did (because more than one key can have that hashCode. You shouldn't - collisions are bad, but it's no problem if you have a handful).
CodePudding user response:
I think you can implement the hashcode like this:
// hashcode
public int hashCode() {
String a = "" this.x "," this.y "," this.layer
return a.hashCode();
}