0

I'm currently working on a TD game with a map editor. Now obviously, you can save and load these maps (or should be able to, at least). Problem is: at some point I'm calling .get() on a HashMap. Unfortunately, the keys that should be the same (logic-wise) are not the same object (in terms of reference), and, according to my previous google research, overriding their .equals method isn't sufficient, since they still return different hashes with .hashCode() (I verified that, they DO return different hashes, while .equals does return true).
(On a side note, that's rather confusing, since the javadoc of HashMap.get(key) only states that they have to be equal)

More specifically, the HashMap contains instances of a class Path of mine as keys, and should return the corresponding list of enemies (= value).

short version of Path (without getters etc.):

public class Path { private List<Tile> tiles = new ArrayList<>(); @Override public boolean equals(Object obj) { //code comparing the two paths } @Override public int hashCode() { //what I still need to implement. ATM, it returns super.hashCode() } } public class Tile { private int x; private int y; //constructor //overrides equals //getters & some convenience methods } 

Now if two Paths are equal, I'd like them to return the same hash code, so that the HashMap returns the correct list of enemies. (I'll make sure not two identical paths can be added).

Now my question:

Do you suggest

  1. using some external library to generate a hash
  2. that I write my own implementation of calculating a hash, or
  3. something else

?

Note that I'd prefer to avoid changing the HashMap to some other type of map, if that would even help solve the problem.

12
  • 1
    your 3) could probably be using your IDE or an IDE in generell to make it generate the hashcode Commented Dec 14, 2016 at 13:54
  • 1
    you can make your hashcode as simple or complex as you like. it is meant as a quick "are these two vaguely simliar" check - to eliminate as many wildly different matches as possible before then diving into your equals method for the ones that survive the hashcode check. So, depending on your system, return tiles.size() might even be appropriate! Commented Dec 14, 2016 at 13:59
  • 2
    worth adding: one mistake people make is to make hashcode overly complex. there's no point having it if it's going to just be as complicated as the actual equals method. like I said: it's meant as a "quick check". Commented Dec 14, 2016 at 14:00
  • 1
    You should give the hashCode contract a read docs.oracle.com/javase/8/docs/api/java/lang/… Commented Dec 14, 2016 at 14:00
  • 1
    @MrSpoon "you can make your hashcode as simple or complex as you like" - you have to respect the hashCode contract. Commented Dec 14, 2016 at 14:05

2 Answers 2

2

You definitely do need to implement your hashCode consistent with equals. IDEs often do decent job generating hashCode and equals. Also consider Objects.equals(...) and Objects.hash(...).

One warning about using Path as keys in the HashMap. You will have to make the class immutable to make it work reliably. Or at least make sure that hashCode of the key does not change. Otherwise you may not able to get you data back even with the same or equal key.

Sign up to request clarification or add additional context in comments.

2 Comments

concerning the immutability of Path: I hope I won't need to do that, since usually, i'll be calling .get with a path retrieved via a reference. This error just occurs after loading a previously created map.
@Misteradi1 If hash code of the path changes you will not be able to retrieve data from the hash map even with the very same instance of the key. Hash map will once again get the hash code of the key and use it to locate bucket with the value. If hash code changes, it won't find the right bucket. Even if the key is the very same instance. Careful with that.
1

The List has a useful method which conveniently is also named list.hashCode(). This will compute the hashCode of all the elements inside the list. So you also have to implement the hashCode for Tile which probably consist of some primitive fields or such.

e.g.

 @Override public int hashCode() { return tiles != null ? tiles.hashCode() : 0; } 

See the docs here

int hashCode()
Returns the hash code value for this list. The hash code of a list is defined to be the result of the following calculation:

 int hashCode = 1; for (E e : list) hashCode = 31*hashCode + (e==null ? 0 : e.hashCode()); 

This ensures that list1.equals(list2) implies that list1.hashCode()==list2.hashCode() for any two lists, list1 and list2, as required by the general contract of Object.hashCode().

2 Comments

Tile is in fact pretty primitive, it just saves 2 integer values (as seen above). Thanks for the tip about list.hashCode()!
Your first block of code can be shortened to Objects.hashCode(tiles).

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.