get(key)是如何工作的,即使在哈希表的大小增加之后也是如此



如果一个哈希表最初的大小是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()调用,该调用也将使用新存储阵列的长度,一切都会很好。