Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java HashMap returning null on get() call

We had observed a NullPointerException when trying to get a value assoicated with a given key in HashMap.

Following is the example code which I will be using to illustrate the problem.

public class Test {

    private Map<String, Integer> employeeNameToAgeMap = new HashMap<String, Integer>();

    public int getAge(String employeeName) { 
        if (!employeeNameToAgeMap.containsKey(employeeName)) {
            int age = getAgeFromSomeCustomAPI(employeeName);
            employeeNameToAgeMap.put(employeeName, age);
        }

        return employeeNameToAgeMap.get(employeeName);
    }
}

Getting a NullPointerException at the last line of the method which is " return employeeNameToAgeMap.get(employeeName);"

As we can see employeeNameToAgeMap is not null and also the caller is not passing employeeName as null (this we have taken in the calling code itself).

This method would be called from different threads and at a very fast rate ( from some timer tasks which are scheduled to run for every 100ms or so )

The reason for this NullPointerException seems to be that the value(age) being put for the given employee is null, but this is not the case as that custom API method (getAgeFromSomeCustomAPI()) is guranteed to return some age for a given employee, even if it was returning null then exception stacktrace should have shown that corresponding line in the logs than the last line.

My only assumption is that while a thread T1 is trying to populate that cache, T2 came up and for some reason it was able to find that cache is having the employeeName already, but when it tried to get the age, it throwed a NPE. But I'm not 100% convinced that while put() operation is in progress for a given key and value, containsKey() for the same key returned true.

I'm aware that this code needs to be enhanced to cater synchronization issues ( by using ConcurrentHashMap or locks), but looking forward to know the real cause for this issue.

I would really appreciate the help.

like image 268
Anil Pradhan Avatar asked Feb 13 '14 12:02

Anil Pradhan


People also ask

Why is MAP get returning null?

Returns null if the HashMap contains no mapping for this key. A return value of null does not necessarily indicate that the HashMap contains no mapping for the key; it's also possible that the HashMap explicitly maps the key to null. The containsKey operation may be used to distinguish these two cases.

Does get return null Java?

In Java, a null value can be assigned to an object reference of any type to indicate that it points to nothing. The compiler assigns null to any uninitialized static and instance members of reference type. In the absence of a constructor, the getArticles() and getName() methods will return a null reference.

What does get function return in HashMap?

The get() method returns the value corresponding to the specified key in the hashmap.

Does HashMap return null not found?

Since some implementations of Map can have null values (like HashMap), it's possible for get to return null even though the key is present. But, if we are just trying to check that the key exists, then we should stick with containsKey.


1 Answers

I believe what you are experiencing is a rehash of the HashMap during your call to

return employeeNameToAgeMap.get(employeeName);

One could believe, that if HashMap#containsKey(key) returns true, it should be guaranteed, that calling HashMap#get(key) should also return a valid value, as long as the key is not removed from the HashMap. This could be argued by the fact, that HashMap#containsKey(key) does indeed check, if the key corresponds to a valid value:

public boolean containsKey(Object key) {
    return getEntry(key) != null;
}

But this is a fatal misconception. HashMap#containsKey(key) does only guarantee, that the key has already been associated to some value sometime before it was called. But it does not guarantee, that HashMap#get(key) will also return the corresponding value, if multiple threads are accessing the map. The reason for this discrepancy is, that other threads accessing HashMap#put(key,value) with any key-value pair, may force a rehash of the HashMap, which results in the recreation of the internal hash table. If such a rehash happens during your call to HashMap#get(key), it is possible, that HashMap#get(key) returns null, even though your HashMap previously returned true when calling HashMap#containsKey(key).

If you just want to avoid the NullPointerException, you could do this:

public class Test {

    private Map<String, Integer> employeeNameToAgeMap = new HashMap<String, Integer>();

    public int getAge(String employeeName) {
        final Integer age = employeeNameToAgeMap.get(employeeName);
        if (age == null) {
            age = getAgeFromSomeCustomAPI(employeeName);
            employeeNameToAgeMap.put(employeeName, age);
        }
        return (int)age;
    }
}

This would of course not make your code thread save, but you would no longer get the NullPointerException you are experiencing now.

like image 116
Balder Avatar answered Nov 03 '22 02:11

Balder