考虑以下代码:
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, and null
作为键是如何相互关联的。所有三个都分配给相同的哈希码吗?如果是,为什么?
由于这三个值看起来不同,一个值的变化会改变另一个值。这是否意味着,只有一个密钥条目被写入
HashMap
或 e1, e2 or null
中?因为所有的都被视为相同的密钥。
null 作为 key 传递并且 null Key 作为特殊情况处理时,
HashMap
不会调用 hashcode。
HashMap
将 null 键放入存储桶 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
get 方法中检查 key 是否作为 null 传递。在存储桶 0 中搜索 null 键的值。
因此,一个
hashmap
对象中只能有一个空键。
如果您传递
null
作为地图键,它将转到0 bucket
。空键的所有值都会去那里。这就是为什么它返回相同的值,因为您提供的所有键都是 null
并且位于 HashMap 的同一个存储桶中。
在 null key 的情况下,Hashmap 实现将其视为特殊情况,不会调用 hashCode 方法,而是将 Entry 对象存储到 0 存储桶位置。
HashMap 每个键只能存储一个值。如果您想存储更多值,则必须使用 MultivalueHashMap(Google Guava 和 Apache Commons Collections 包含此类映射的实现)。
e1 和 e2 的值为 null,因为您没有为它们分配任何对象。因此,如果您使用这些变量,则该映射条目的键也是 null,这会导致您的结果。 Null 没有任何哈希码,但可以作为 HashMap 中的键(还有其他 Map 实现不允许 Null 作为键)。
当您将 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;
}
HashMap 中的所有插入:
map.put(e2, "25");
map.put(e1, "");
map.put(null, null);
map.put(null, "30");
将
null
作为密钥,因为在您的代码中 e1 和 e2 设置为 null
。使用相同的键只能存储一个值。在这种情况下,存储在映射中的 null
键的值会在代码中每次 put
调用后更新。