为什么HashTable不能存null键和null值,而HashMap却可以?

为什么HashTable不能存null键和null值,而HashMap却可以?

首先明确一点:

HashMap可以存放一个键是null,多个值是null 的对象,

而Hashtable则不可以存放键为null,或者是值为null的对象

为什么HashTable不能存null键和null值?

原因:

当value值为null时主动抛出空指针异常因为key值会进行哈希计算,如果为null的话,无法调用该方法,还是会抛出空指针异常Hashtable的put方法源码如下:

public synchronized V put(K key, V value) {

// 确认值不为空

if (value == null) {

throw new NullPointerException(); // 如果值为null,则抛出空指针异常

}

// 确认值之前不存在Hashtable里

Entry tab[] = table;

int hash = key.hashCode(); // 如果key如果为null,调用这个方法会抛出空指针异常

int index = (hash & 0x7FFFFFFF) % tab.length;//计算存储位置

//遍历,看是否键或值对是否已经存在,如果已经存在返回旧值

@SuppressWarnings("unchecked")

Entry entry = (Entry)tab[index];

for(; entry != null ; entry = entry.next) {

if ((entry.hash == hash) && entry.key.equals(key)) {

V old = entry.value;

entry.value = value;

return old;

}

}

addEntry(hash, key, value, index);

return null;

}

为什么HashMap可以存null键和null值?

HashMap在put的时候会调用hash()方法来计算key的hashcode值,

HashMap的hash算法指定了:当key==null时返回的值为0。

因此key为null时,hash算法返回值为0,不会调用key的hashcode方法。

//当key==null时hash的值为0

//一个hashmap对象只会存储一个key为null的键值对,因为它们的hash值都为0

static final int hash(Object key) {

int h;

return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

}

// 将键值对放入table中时,不会校验value是否为null。

// 因此一个hashmap对象可以存储多个value为null的键值对

public V put(K key, V value) {

return putVal(hash(key), key, value, false, true);

}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,

boolean evict) {

Node[] tab; Node p; int n, i;

if ((tab = table) == null || (n = tab.length) == 0)

n = (tab = resize()).length;

if ((p = tab[i = (n - 1) & hash]) == null)

tab[i] = newNode(hash, key, value, null);

else {

Node e; K k;

if (p.hash == hash &&

((k = p.key) == key || (key != null && key.equals(k))))

e = p;

else if (p instanceof TreeNode)

e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);

else {

for (int binCount = 0; ; ++binCount) {

if ((e = p.next) == null) {

p.next = newNode(hash, key, value, null);

if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st

treeifyBin(tab, hash);

break;

}

if (e.hash == hash &&

((k = e.key) == key || (key != null && key.equals(k))))

break;

p = e;

}

}

if (e != null) { // existing mapping for key

V oldValue = e.value;

if (!onlyIfAbsent || oldValue == null)

e.value = value;

afterNodeAccess(e);

return oldValue;

}

}

++modCount;

if (++size > threshold)

resize();

afterNodeInsertion(evict);

return null;

}

map.put(key1, value1):

* 首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组 中的存放位置。

* 如果此位置上的数据为空,此时的key1-value1 添加成功。---- 情况1

* 如果此位置上的数据不为空,(意味着此位置上存在一个或多 个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据

* 的哈希值:

* 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1 添加成功。----情况2

* 如果key1的哈希值和已经存在的某一个数据(key2-value2) 的哈希值相同,继续比较:调用key1 所在类的equals(key2)

* 如果equals()返回faLse:此时key1-value1添加成功。----情况3

* 如果equals().返回true:使用value1替换value2。

* 补充:关于情况2和情况3:此时key1-value1 和原来的数据以链表的方式存储。

相关推荐

德国世杯夺冠奖金 每人或得182万元
365bet足球数据直播

德国世杯夺冠奖金 每人或得182万元

📅 08-06 👁️ 8882
黑天使 ELEKTRA
外勤365老版本下载怎样下载

黑天使 ELEKTRA

📅 07-13 👁️ 3415
淘宝省钱卡自动续费关闭指南,三步搞定不花冤枉钱
365bet足球数据直播

淘宝省钱卡自动续费关闭指南,三步搞定不花冤枉钱

📅 11-27 👁️ 7809