2

直接来自这个javadoc:

HashMap 的实例有两个影响其性能的参数:初始容量和负载因子。容量是哈希表中的桶数,初始容量只是哈希表创建时的容量。负载因子是哈希表在其容量自动增加之前允许达到的程度的度量。当哈希表中的条目数超过负载因子和当前容量的乘积时,对哈希表进行重新哈希(即重建内部数据结构),使哈希表的桶数大约增加一倍。

假设哈希表中的条目数超过了负载因子和当前容量的乘积……数据结构被重新哈希,桶的数量大约是两倍……好吧……如果哈希码() 方法保持不变......增加桶的数量如何减少查找等待时间?如果哈希码相同,则查找将在同一个存储桶中完成,即使旁边有另一个新创建的空存储桶。不是吗?(确定不是它,但为什么?)提前谢谢。

4

3 回答 3

8

虽然hashCode()返回值将保持不变,但这些值并不直接用于查找存储桶。它们被转换为 buckets 数组的索引,通常通过index = hashCode() % tableSize. 考虑一个大小为 10 的表,其中有两个键的哈希值为 5 和 15。在表大小为 10 时,它们最终位于同一个桶中。将表的大小调整为 20,它们最终位于不同的存储桶中。

于 2013-06-30T08:27:33.370 回答
3

这是一个简化版本,但想象一下您找到的存储桶:

int bucket = hash % 8; //8 buckets

重新散列后将变为:

int bucket = hash % 16;//16 buckets

如果您的初始哈希为 15,则在重新哈希之前它在存储桶 7 中,但在重新哈希之后它在存储桶 15 中。

于 2013-06-30T08:25:21.500 回答
1

这个问题已经被正确回答了。为了更深入地理解,这里是来自java.util.HashMap.

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable
{

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

        /**
         * Creates new entry.
         */
        Entry(int h, K k, V v, Entry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }

        ... methods...
    }


    /**
     * The table, resized as necessary. Length MUST Always be a power of two.
     */
    transient Entry<K,V>[] table;


    /**
     * Returns index for hash code h.
     */
    static int indexFor(int h, int length) {
        return h & (length-1);
    }


    /**
     * Adds a new entry with the specified key, value and hash code to
     * the specified bucket.  It is the responsibility of this
     * method to resize the table if appropriate.
     */
    void addEntry(int hash, K key, V value, int bucketIndex) {
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? hash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }

        createEntry(hash, key, value, bucketIndex);
    }


    /**
     * Like addEntry except that this version is used when creating entries
     * as part of Map construction or "pseudo-construction" (cloning,
     * deserialization).  This version needn't worry about resizing the table.
     */
    void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry<K,V> e = table[bucketIndex];
// *** you see how the next line builds a linked list of entries with the same hash code ***
        table[bucketIndex] = new Entry<>(hash, key, value, e);
        size++;
    }



    /**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     */
    public V put(K key, V value) {
        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;
    }



}
于 2013-06-30T08:41:07.753 回答