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

时间:2021-06-16 23:05:54

标签: java hashmap

在 Java 7 HashMap 实现中,在 resize 方法中,它调用 transfer 将旧元素移动到新表。为什么他们编写了一个新方法而不是使用所有旧元素调用 put?由于 threshold,不会再次触发调整大小。调用 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);
        }
    }
}

1 个答案:

答案 0 :(得分:5)

一个重要的区别是 dynamic_cast 可以通过两种方式利用每个条目已经存在 template <typename To, typename From> To fastest_cast( From && from ); struct A { virtual ~A() = default; }; struct B : A {}; struct C : virtual A {}; int main() { B b; fastest_cast<B*>( (A*)&b ); //expected static_cast inside C c; fastest_cast<C*>( (A*)&c ); //expected dynamic_cast inside } 对象这一事实:

  • 它可以重用 fastest_cast 对象本身以避免必须分配新对象(从而避免内存分配,从而降低 GC 压力)。
  • 它可以重用存储在 bidId 对象中的 transfer 值,从而避免必须对地图中已有的每个键调用 Entry(理论上这可能是一项昂贵的操作)。

基本上:如果仅根据 Entry 实现 hash,则必须重新做很多很容易避免的工作。

JDK 的更高版本具有明显更复杂的 Entry 实现,其中实现了更复杂的 Object.hashValue 方法(或等效方法)。

还值得指出的是,在 JDK 本身中完成时,即使以不那么“简单”的代码为代价的微小性能提升通常也是值得的:因为 resize 基本上用于那里的每个 Java 程序,具有以稍微降低可读性为代价尽可能提高性能通常是值得的权衡。同样的推理同样适用于我们“凡人”编写的大多数其他软件。

相关问题