HashMap中允许空键和空值

40

请考虑下面的代码:

import java.util.*;

class Employee {
    
    String name;
    
    public Employee(String nm) {
        this.name=nm;
    }
}

public class HashMapKeyNullValue {
    
    Employee e1;
    
    public void display(){

        Employee e2=null;
        Map map=new HashMap();

        map.put(e2, "25");
        System.out.println("Getting the Value When e2 is set as KEY");
        System.out.println("e2 : "+map.get(e2));
        System.out.println("e1 : "+map.get(e1));
        System.out.println("null : "+map.get(null));

        map.put(e1, "");
        System.out.println("Getting the Value when e1 is set as KEY");
        System.out.println("e2 : "+map.get(e2));
        System.out.println("e1 : "+map.get(e1));
        System.out.println("null : "+map.get(null));

        map.put(null, null);   // null as key and null as value
        System.out.println("Getting the Value when setting null as KEY and null as value");
        System.out.println("e2 : "+map.get(e2));
        System.out.println("e1 : "+map.get(e1));
        System.out.println("null : "+map.get(null));

        map.put(null, "30");
        System.out.println("Getting the Value when setting only null as KEY");
        System.out.println("e2 : "+map.get(e2));
        System.out.println("e1 : "+map.get(e1));
        System.out.println("null : "+map.get(null));
    }
    
    public static void main(String[] args) {
        
        new HashMapKeyNullValue().display();
        
    }
}

程序的输出为:

Getting the Value When e2 is set as KEY
e2 : 25
e1 : 25
null : 25
Getting the Value when e1 is set as KEY
e2 : 
e1 : 
null : 
Getting the Value when setting null as KEY and null as value
e2 : null
e1 : null
null : null
Getting the Value when setting only null as KEY
e2 : 30
e1 : 30
null : 30

这里介绍了e1,e2和null作为键的关系。它们是否都分配给相同的哈希码?如果是,为什么?

由于这三个值看起来都不同,更改其中一个值会更改其他值。这是否意味着只有一个键的条目被添加到HashMap中,即e1,e2或null?因为它们都被视为相同的键。


e1和e2的值为null。Java是按值传递,只会传递变量的值,当该变量被用作调用参数时。也就是说,变量在函数内部永远不会被重新赋值,并且函数中的参数名称与任何变量无关。 - Joop Eggen
可能是HashMap的键为空的重复问题。 - Alex
5个回答

74

HashMap在将null作为键传递时不会调用hashcode方法,并且将null键处理为特殊情况。

Put方法

HashMapnull键放入桶0中,并将null映射为传递的值。HashMap使用链表数据结构实现此操作。 HashMap在内部使用链表数据结构。

HashMap使用的链表数据结构是在HashMap.java中的静态类中定义的。

static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        final int hash;
}

在 Entry 类中,K 被设置为 null,并将传递给 put 方法的值映射到该值。

获取方法

当在 HashMap 的获取方法中检查键是否为 null 时,在 bucket 0 中搜索 null 键的值。

因此在一个 hashmap 对象中只能有一个 null 键。


3
FYI,答案完全正确,但是自Java 1.8以来,HashMap的实现有一些细微的变化。现在可以使用平衡树作为“桶”,而不是链表。 - fascynacja

8
如果您将null作为映射键传递,它将进入0 bucket。所有空键的值都会进入那里。这就是为什么它返回相同的值,因为您提供的所有键都是null,并且在您的HashMap的同一个桶中。

如果我插入了两个键为NULL的值v1和v2,那么在获取时我会得到哪一个?总是最后一个吗? - dinesh kandpal
2
@dineshkandpal 是的,总是最后一个(v2) - Alex Torson

4

如果键值为null,HashMap实现会将其视为特殊情况,并且不调用hashCode方法,而是将Entry对象存储到0号桶位置。


2

HashMap只能为每个键存储一个值。如果您想存储更多的值,您需要使用MultivalueHashMap(Google Guava和Apache Commons Collections都包含此类映射的实现)。

e1和e2的值为null,因为您没有将任何对象分配给它们。因此,如果您使用这些变量,该映射条目的键也为null,这导致了您的结果。Null没有任何哈希码,但在HashMap中可以容忍作为键(还有其他不允许Null作为键的Map实现)。


1
当您将NULL放入HashMap中时,会进行特殊检查,以确定您是否尝试将NULL作为键放入(称为putForNullKey())。这是一种特殊情况,并且不像您尝试放置某些非空对象那样工作,正如您所看到的,它甚至不进行哈希计算。
public V put(K key, V value) {
    if (table == EMPTY_TABLE) {
        inflateTable(threshold);
    }
    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;
}

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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接