1

What I am trying to do here is to fill a HashMap with objects Obj using the key Key and when I finish I want to have access to those values according to any any of the possible keys. I have written the following code and what happened is that although the first show really shows the values I want, the second one raises a NullPointerException.

import java.util.*;

public class My{
public static void main(String[] args){
    Map<Key,Obj> myMap = new HashMap<Key,Obj>();

    Obj ob1 = new Obj("Nick",19);
    Obj ob2 = new Obj("George",17);

    Key key1 = new Key(1,2);
    Key key2 = new Key(2,1);

    myMap.put(key1,ob1);
    myMap.put(key2,ob2);

    myMap.get(key1).show();
    myMap.get(new Key(1,2)).show();

}

I can tell that somehow Java cannot tell that new Key(1,2) is equal with key1, but I cannot think of how can I overcome this issue.

public class Obj{

    private String name;
    private int age;

    Obj(String name, int age){
        this.name = name;
        this.age = age;
    }

    public void show(){
         System.out.println(name + " " + age);
    }
}

These are the classes I use

import java.* ;

public class Key{

    public int x,y;

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

    public boolean equals(Key d){
        if ((this.x == d.x)&&(this.y == d.y)){
            return true;
        }
        else{
            return false;
        }
    }
}
Ahmad Al-Kurdi
  • 2,248
  • 3
  • 23
  • 39
al96
  • 13
  • 3
  • 5
    You need to implement `hashCode` in `Key` – janos Dec 04 '17 at 20:47
  • 1
    It's also worth learning the pattern that any time you see `if (condition) return true; else return false;` you can replace it with `return condition;` - so your `equals` method can be `return this.x == d.x && this.y == d.y;`. – Jon Skeet Dec 04 '17 at 20:49
  • 1
    Whenever overriding `equals` you should override `hashCode` with the same functionality too, exactly for **that reason**. – Zabuzard Dec 04 '17 at 20:49
  • Additionally, you're not overriding the `equals` method inherited from `Object` - the parameter type is wrong for that. – Jon Skeet Dec 04 '17 at 20:49
  • @janos *and* `equals()` – Bohemian Dec 04 '17 at 20:51
  • Questions like this are typically marked as duplicate of https://stackoverflow.com/questions/27581/what-issues-should-be-considered-when-overriding-equals-and-hashcode-in-java – DavidW Dec 04 '17 at 20:51
  • ok, but when I use key1 as a key it returns the value, so I thought it can somehow fetch the value by just having the object Key. Or maybe not.. Either way thanks for answering – al96 Dec 04 '17 at 20:51
  • It can, if you override `hashCode` correctly. The `HashMap` stores its keys based on their **hash code**. If you say *"search for that key"* it will compute its hash code and check what is written for this number. However if you don't override `hashCode` correctly, your new elements, though equal per `equal`, will return a **different hash code** and thus the map does **not find** something written at that number. Because of that the contract is if `equal` says `true`, then `hashCode` must also return the **same value** for both objects. – Zabuzard Dec 04 '17 at 20:57

1 Answers1

2

You have two problems in your implementation of Key. First, the equals' method signature is wrong - it should be public boolean equals(Object). This error would have been easily noticeable if you had used the @Override annotation. Second, you should also override the hashCode() method:

@Override
public boolean equals(Object o) {
    if (o == null || getClass() != o.getClass()) {
        return false;
    }
    Key key = (Key) o;
    return x == key.x && y == key.y;
}

@Override
public int hashCode() {
    return Objects.hash(x, y);
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • 1
    Can you explain the issue to OP? For example why is `hashCode` necessary, why does it solve the issue? What has this to do with the `HashMap` at all? – Zabuzard Dec 04 '17 at 21:00