0

I don't understand why this code:

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class Main {
    public static void main(String[] args) {
        Map<Person, String> map = new HashMap<>();
        Person person = new Person("person");
        map.put(person, "");
        person.name = "person2";  // key's property changed
        System.out.println(map.containsKey(new Person("person")));
        System.out.println(map.containsKey(new Person("person2")));
    }

    static class Person {
        String name;

        Person(String name) {
            this.name = name;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return Objects.equals(name, person.name);
        }

        @Override
        public int hashCode() {
            return Objects.hash(name);
        }

    }
}

prints false false. When I put Person object in a map, hash should be calculated. Then key's property is changed. While invoking containsKey, hashes should be compared and new Person("person2") has the same hash as previously inserted key. What happens that in both cases containsKey evaluates to false?

k13i
  • 4,011
  • 3
  • 35
  • 63
  • Why use `Objects.hash(name)` instead of `name.hashCode()`? – phflack Jan 30 '18 at 18:40
  • 4
    And HashMaps don't expect key hashcodes to change, I don't think there's anything in place to update which bucket it's stored in _(using the API in an undefined way may result in undefined behaviour)_ – phflack Jan 30 '18 at 18:42
  • Or why not just make the hashmap take in String keys and pass the name as the key? – Orch Jan 30 '18 at 18:43
  • @phflack Is there a difference in that? Does it matter? – Thiyagu Jan 30 '18 at 18:43
  • @user7 No, it's just unnecessary complexity, which can hinder debugging – phflack Jan 30 '18 at 18:44
  • 1
    you cannot mutate the properties that are part of hashcode generation – Thiyagu Jan 30 '18 at 18:44
  • `Objects.hash(name)` invokes internally `name.hashCode()` so that should not be the case. I know that mutating properties of a key is not good idea, but I don't understand internal implementation... – k13i Jan 30 '18 at 18:52
  • 2
    I was in the middle of writing an answer when this was closed, but mutating a `Person` that has already been set as a key to the `Map` **does not** change the bucket that it has been inserted in. However, it **does** change the object's `hashCode`. Because of this, checking if the `Map` contains `new Person("person")` gets close to finding the object (it begins to check the same bucket that the object is located in), but the `hashCode`s differ, so it returns `false`. – Jacob G. Jan 30 '18 at 18:53
  • @Orch I didn't pass the `String` because it's immutable so this problem wouldn't happen – k13i Jan 30 '18 at 18:54
  • @JacobG. When we call for `new Person("person")` it gets to the right bucket - do u mean it returns false, because it is no longer `equal`? (`person` != `person2`)`? – Thiyagu Jan 30 '18 at 19:11
  • @user7 I'd post the source code for `Map#getNode` here if I could, but yes, you're correct! Take OP's code and always return `true` in `Person#equals`, and then `map.containsKey(new Person("person"))` returns `true`. – Jacob G. Jan 30 '18 at 19:14
  • @JacobG. So the hashcodes differing does not play any role for `person` string as it got to the correct bucket somehow and everything from here on is only `equals` – Thiyagu Jan 30 '18 at 19:16
  • 2
    @user7 When `new Person("person")` was added to the `Map`, it was put in some bucket `X`. Changing the object's `name` to `"person2"` does **not** move it from bucket `X` into another bucket. Therefore, checking `map.containsKey(new Person("person"))` navigates to bucket `X`, and then checks `equals` on all objects within the bucket, returning `"person2".equals("person")`: `false` – Jacob G. Jan 30 '18 at 19:19
  • 1
    @JacobG. Clear. That's what I actually meant btw. Thanks – Thiyagu Jan 30 '18 at 19:24
  • 1
    @JacobG. thanks for explanation. – k13i Jan 30 '18 at 19:31

0 Answers0