如果一个哈希表最初的大小是8,我们达到了负载因子,它的大小会增加一倍。如何仍然能够检索原始值。。。假设我们有一个散列函数key(8)转换为12345作为散列值,我们用8对其进行模化,得到索引7。。。现在,当哈希表大小增长到16。。。对于键(8),我们得到12345。。如果我们在16之前修改它,我们会得到不同的答案!那么,我如何仍然检索原始密钥(8)
这不是Java特有的-当哈希表增长时(在我所知道的大多数实现中),它必须重新评估所有哈希对象的键,并根据现在可用的bucket数量将它们放入新的、正确的bucket中。
这也是为什么调整哈希表的大小通常被认为是一项"昂贵"的操作(与许多其他操作相比),因为它必须访问其中存储的所有项目
用于查找值的哈希值来自密钥对象本身,而不是容器。
这就是为什么Map中用作键的对象必须是不可变的。如果hashCode()
发生更改,您将无法再次找到密钥或值。
它完全依赖于实现,但在必要时会进行rehash。
查看由resize()
方法调用的transfer()
方法中HashMap类的源代码。
/**
* Transfers all entries from current table to newTable.
*/
void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null;
do {
Entry<K,V> next = e.next;
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
在这个HashTable实现中,您可以精确地跟踪每个条目是如何存储在新的(两倍大)存储阵列中的。新阵列的容量用于确定每个项目将存储在哪个插槽中。密钥的散列码不会改变(事实上,它甚至没有被重新计算,而是从每个Entry
对象中名为hash
的公共字段中检索出来,存储在其中),改变的是indexFor()
调用的结果:
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
return h & (length-1);
}
它获取散列码和新存储阵列的长度,并返回新阵列中的索引。
因此,客户端对get()
的新调用将通过相同的indexFor()
调用,该调用也将使用新存储阵列的长度,一切都会很好。