-10

I'm creating a game. When I'm reading a LinkedHashMap, it gives me an NPE.

I fill LinkedHashMap hm like this:

for (String s : line.split("")) {
    if (s.contains("*")) {
        hm.put(new Coordinates(xa-32, ya), "gray");
    } else if (s.contains("#")) {
        hm.put(new Coordinates(xa-32, ya), "black");
    }
    // other code...
}

Later I try to get color from the HashMap like this, and get an NPE:

if ((keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_A)
    && isPainted && hm.get(new Coordinates(x - 32, y)).equalsIgnoreCase("gray")) {
    x -= 32;
}

Full code here:

tar
  • 1,538
  • 1
  • 20
  • 33
Zygimantas
  • 33,165
  • 6
  • 21
  • 23
  • 5
    please copy full error message. – libik Mar 24 '14 at 19:04
  • 1
    Yes, post a StackTrace – keuleJ Mar 24 '14 at 19:05
  • 2
    and add the important code (where you get the NPE) we don't need all your classes – Marco Acierno Mar 24 '14 at 19:05
  • Please be clear with your question. – RKC Mar 24 '14 at 19:06
  • 1
    Just linking to code in pastebin is poor form. You should reduce the problem to a short but complete program demonstrating the problem, and include that (and the stack trace) directly into your question. (Looking at the code, I suspect you should be able to reproduce it very simply indeed, and it has nothing to do with most of the 400+ lines in the first file.) – Jon Skeet Apr 24 '14 at 16:28

6 Answers6

10

On this line hm.get(new Coordinates(x, y - 32)).equalsIgnoreCase("gray")), it is not possible that hm contains newly created Coordinates.

When you create new object, for example Coordinates c = new Coordinates(x, y - 32);, in memory is created that object and the variable c holds reference to that memory, not object itself.

Because of it, look at this code :

Coordinates c1 = new Coordinates(x, y - 32); //c1 holds reference to memory, something like "a8wgge8h"
Coordinates c2 = new Coordinates(x, y - 32); //c2 holds also reference to memory, someting like "a8w12238h"
if (c1 != c2){
   System.out.println("Yes, it is true, c1 is not c2, there are two objects with same properties, but they are not same, like human twins - they look same, but two people actually exists");
}

Therefore you cant find anything in hm.get(new Coordinates(x, y - 32)), because it does not look for coordinate which has same x,y, it looks for coordinate with same reference to the memory. And it cannot exists, because you just create new object, java associated it new memory address, something like abnbn147 and then your list/set looks for object with address abnbn147, which cannot be stored there, because you just have just created it.

This hm.get(new Coordinates(x, y - 32)) always return null. If you call method on null, it ends with NullPointerException, which happens with calling method equalsIgnoreCase on null object I was talking about.

libik
  • 22,239
  • 9
  • 44
  • 87
  • 1
    A simple way to fix the equalsIgnoreCase NPE is to reverse the condition `if ("gray".equalsIgnoreCase(hm.get(new Coordinates(x - 32, y)))` – Stephan Apr 25 '14 at 12:28
  • 1
    @Stephan - nonono, you do not understand the problem. This code `hm.get(new Coordinates(x - 32, y))` ALWAYS RETURN NULL. Your solution does not solve problem, it only moves NPE problem into another problem (and it is, why my `"gray".equalsIgnoreCase(hm.get(new Coordinates(x - 32, y)))` always return false?) – libik Apr 25 '14 at 12:39
  • @libik Map compare keys with **equals()** and **hashCode()**, so once properly overriden, `hm.get(new Coordinates(...))` will work as expected – Nikita Bosik Sep 18 '17 at 16:39
5

To make it work, in class Coordinates implement equals and hashCode methods.

Example:

@Override
public int hashCode()
{
  return 10000*x+y;
}

@Override
public boolean equals(Object obj)
{
  if(obj == null)
    return false;
  if (getClass() != obj.getClass())
    return false;
  Coordinates other = (Coordinates)obj;
  return x == other.getX() && y == other.getY();
}

Longer explanation:

LinkedHashMap uses equals method to compare your key with keys already in map. Default equals method compares object references, so if you have

Coordinates a = new Coordinates(1, 1); 
Coordinates b = new Coordinates(1, 1);

a.equals(b) returns false. If you not override equals method, hashmap will not find item with your key.

hashCode() must be implemented if you override equals() method. Whenever a.equals(b), then a.hashCode() must be same as b.hashCode().

http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#equals(java.lang.Object) http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode()

bigGuy
  • 1,732
  • 1
  • 22
  • 37
2

This is because you are comparing objects. TO get your desired value for a given key LinkedHashMap uses hashcode and toString methods. However for given 2 objects which refers to 2 different instances of the class can't be same. Root cause of your problem is at hm.put(new Coordinates(xa-32, ya), "gray"); and hm.get(new Coordinates(x - 32, y)) because both objects are different.

However in order to solve this problem you can override hashCode and toString methods in Coordinates class in a such way that gives you same hashcode every time for given cordinates. Hope this will help and provide you a right direction.

For Example.

public class Cordinates {

int x, y;

public Cordinates(int x, int y) {
    super();
    this.x = x;
    this.y = y;
}

public Cordinates() {
    super();
}

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + x;
    result = prime * result + y;
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Cordinates other = (Cordinates) obj;
    if (x != other.x)
        return false;
    if (y != other.y)
        return false;
    return true;
}

@Override
public String toString() {
    return "Cordinates [x=" + x + ", y=" + y + "]";
}

}

er_suthar
  • 319
  • 1
  • 8
1

In the line hm.get(new Coordinates(x - 32, y)).equalsIgnoreCase("gray") you have no guarantee that the hashmap contains anything that corresponds to the key new Coordinates(x - 32, y) so this is very likely to return null (particularly dependent on how Coordinates implements equals()). This will lead to the comparison null.equalsIgnoreCase("gray"). Hence the null pointer exception. You need to remove the hm.get() statement from the if statement and include a null check.

phn
  • 1,720
  • 1
  • 14
  • 10
1

Whenever I encounter a NullPointerException that is weird, it is %90 because of uninitialized List type of objects. Try writing

LinkedHashMap<Coordinates, String> hm = new LinkedHashMap<>();

before you add some values to your LinkedHashMap

padawan
  • 1,295
  • 1
  • 17
  • 45
0

hm.get(new Coordinates(x, y - 32)) returns null in your case. and hence

hm.get(new Coordinates(x, y - 32)).equals means calling a method on null throws NPE.

why hm.get(new Coordinates(x, y - 32)) returns null in your case ?

when you add key to the hashmap, it creates a hashcode for that and stores it in its cache and while retrieving it refers to the corresponding hashcode available in the cache.

so while adding new coordinates , cordinate object hashcode will be added to hashmap.

in your case cordinate class has not overriden hashcode and equlas method properly.

hence when you create new coordinate object its hashcode will be different for each object even though they are identical.

so even if you create 2 identical object , your hashcode for 2 identical cordinate object will be different because no hashcode overriden and hence Object class hashcode is taken.

How to Avoid this

Override hashcode method inside your coordinate class considering all the attributes for hashcode

and use prime number for calculation

public int hashCode()
{
  return 31*x+y;
}

and override equals method as below

public boolean equals(Object obj)
{
  if(obj == null)
    return false;
  if (!(obj instanceof this))
    return false;

  Coordinates cordinate= (Coordinates)obj;
  return x == cordinate.getX() && y == cordinate.getY();
}

So now when you create 2 identical object their hashcode remains the same and while entering those object as key to map, it identifies them as identical.

and if you retrieve value from map by passing key as a new object(which is identical with attributes to the already inserted key) then it gets the value which is not null.

Final Point

hm.put(new Coordinates(x, y - 32),"value");

here new coordinate object has been created , its hashcode is cached by hashmap which will be used later for comparing while retriveing.

hm.get(new Coordinates(x, y - 32)); 

this will also creates a new coordinate object and its hashcode will be same if we have overriden the hashcode and hence it gets the value and retunns "value" for us.

if not overriden then its new hashcode value will be different and while retriving hashmap searches in its cache but not available and hence returns null.

so make sure hashcode and equals method are properly overriden on the cordinate class.

Karibasappa G C
  • 2,686
  • 1
  • 18
  • 27