为什么 Java HashMap 实现在调整大小时使用 transfer() 而不是 put()?

Run*_*hen 4 java hashmap

在 Java 7HashMap实现中resize,它调用transfer将旧元素移动到新表的方法。为什么他们编写了一个新方法而不是put使用所有旧元素进行调用?由于阈值,不会再次触发调整大小。调用put使代码更清晰。

/**
 * 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Joa*_*uer 5

一个重要的区别是transfer可以通过Entry两种方式利用每个条目的对象已经存在的事实:

  • 它可以重用Entry对象本身以避免必须分配新对象(从而避免内存分配,从而减少 GC 压力)。
  • 它可以重用hash存储在Entry对象中的值,从而避免必须调用Object.hashValue映射中已经存在的每个键(理论上这可能是一项昂贵的操作)。

基本上:如果resize只是简单地实现put它就必须重新做很多可以轻松避免的工作。

JDK 的更高版本具有更复杂的HashMap实现,其中实现了更复杂的transfer方法(或等效方法)。

还值得指出的是,在 JDK 本身中完成时,即使以不那么“简单”的代码为代价的微小的性能提升通常也是值得的:因为HashMap基本上每个 Java 程序都使用它,因此它在可读性稍差的代价通常是值得权衡的。同样的道理也不能等同于大多数其他软件,我们“凡人”写申请。