Appropriate Keys

Both objects with reference based and value based definitions of equals and hashCode are "appropriate" to use as keys in HashMaps.

The most important thing to be careful of using objects whereequals and hashCode are value based, but the object itself is mutable.

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

void main() {
    new Main().main();    
}

class Pos {
    int x;
    int y;

    Pos(int x, int y) {
        this.x = x;
        this.y = y;
    }

    // equals and hashCode here are defined in terms of X and Y
    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }

    @Override
    public boolean equals(Object o) {
        return o instanceof Pos p &&
            this.x == p.x &&
            this.y == p.y;
    }
}

class Main {
    void main() {
        var m = new HashMap<Pos, String>();

        // Therefore we can get and put values using a Pos
        // as a key.
        var p = new Pos(4, 5);
        m.put(p, "Slippery Ice");

        System.out.println(
            m.get(p)
        );

        // But if we were to mutate the object
        p.x = 99;

        // then the original object might be in the wrong bucket
        // inside the hash map!
        System.out.println(
            m.get(p)
        );

        // And because the key is stored, even if it is in the right bucket
        // the equals check might not function correctly
        System.out.println(
            m.get(new Pos(4, 5))
        );
    }
}

So while Pos has a "value based identity," you can muck it up by directly changing values. HashMaps assume that when something is used as a key its equals and hashCode are stable and will not change later in the program.

If something has a value-based definition of equals and hashCode but can be changed, that is inappropriate to use as a key.