Mengjun
Mengjun

Reputation: 3197

What is the advantage of null keys mechanism change of HashMap in JDK 1.6 or above version?

I found that some code is changed for null keys in class HashMap in JDK 1.6 or above version compared to the previous JDK version, like 1.5.

In JDK1.5, a static final Object named NULL_KEY is defined: static final Object NULL_KEY = new Object();

Methods, including maskNull, unmaskNull, get and put etc, will use this object.

See

static final Object NULL_KEY = new Object();
static <T> T maskNull(T key) {
    return key == null ? (T)NULL_KEY : key;
}
static <T> T unmaskNull(T key) {
    return (key == NULL_KEY ? null : key);
}

public V get(Object key) {
    Object k = maskNull(key);    
    int hash = hash(k);        
    int i = indexFor(hash, table.length); 
    Entry<K,V> e = table[i];             
    while (true) {                        
        if (e == null)
            return null;
        if (e.hash == hash && eq(k, e.key))
            return e.value;
        e = e.next;
    }
}

public V put(K key, V value) {
    K k = maskNull(key);                             
    int hash = hash(k);                             
    int i = indexFor(hash, table.length);             
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        if (e.hash == hash && eq(k, e.key)) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);              
            return oldValue;
        }
    }

    modCount++;                                       
    addEntry(hash, k, value, i);                      
    return null;
}

However, such Object (NULL_KEY) is not used in JDK 1.6 or above version.

Instead, two new methods named getForNullKey() and putForNullKey(value) is added, which are applied in get and put method as well.

See the source code as follows:

 public V get(Object key) {
    if (key == null)
        return getForNullKey();
    Entry<K,V> entry = getEntry(key);

    return null == entry ? null : entry.getValue();
}

 private V getForNullKey() {
    for (Entry<K,V> e = table[0]; e != null; e = e.next) {
        if (e.key == null)
            return e.value;
    }
    return null;
 }

public V put(K key, V value) {
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key);
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }

    modCount++;
    addEntry(hash, key, value, i);
    return null;
}

/**
 * Offloaded version of put for null keys
 */
private V putForNullKey(V value) {
    for (Entry<K,V> e = table[0]; e != null; e = e.next) {
        if (e.key == null) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }
    modCount++;
    addEntry(0, null, value, 0);
    return null;
}

Change always has its reason for changing, such as improving the performance etc. Please help me out with the following 2 question

Q#1 ==> Why this change is made, is there some scenario that the null keys of HashMap implemented in JDK 1.5 encouneters issue ?

Q#2 ==> What is the advantage of null keys mechanism change of HashMap in JDK 1.6 or above version?

Upvotes: 7

Views: 418

Answers (1)

auselen
auselen

Reputation: 28087

Documentation for private V getForNullKey() says

Offloaded version of get() to look up null keys. Null keys map to index 0. This null case is split out into separate methods for the sake of performance in the two most commonly used operations (get and put), but incorporated with conditionals in others.

Upvotes: 2

Related Questions