簡體   English   中英

為什么使用 1<<4 而不是 16?

[英]Why use 1<<4 instead of 16?

java.util.HashMap的 OpenJDK 代碼包括以下行:

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

為什么在這里使用1 << 4而不是16 我很好奇。

1 << 4而不是 16 不會改變這里的行為。 這樣做是為了強調數字是 2 的,而不是完全任意的選擇。 因此,它提醒開發人員嘗試使用不同的數字,他們應該堅持這種模式(例如,使用1 << 31 << 5 ,而不是20 ),這樣他們就不會破壞所有依賴它作為 2 的冪的方法. 上面有一條評論:

/**
 * The default initial capacity - MUST be a power of two.
 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

無論java.util.HashMap增長到多大,它的表容量(數組長度)都保持為 2 的冪。 這允許使用快速按位與運算 ( & ) 來選擇存儲對象的存儲桶索引,如訪問表的方法中所示

final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) { /// <-- bitwise 'AND' here
        ...

其中, n是表容量,並且(n - 1) & hash包裝了散列值以適應該范圍。

更多詳情

哈希表有一個“桶”數組( HashMap稱它們為Node ),其中每個桶存儲零個或多個映射的鍵值對。

每次我們getput一個鍵值對時,我們都會計算該鍵的哈希值。 哈希是一些任意(可能很大)的數字。 然后我們從散列中計算一個桶索引,以選擇對象的存儲位置。

大於桶數的哈希值被“環繞”以適應表格。 例如,表容量為 100 個桶,哈希值 5、105、205 將全部存儲在桶 5 中。可以將其想象為圓周上的度數,或鍾面上的小時數。

(哈希值也可以是負數。-95 的值可能對應於存儲桶 5 或 95,具體取決於它是如何實現的。確切的公式並不重要,只要它在存儲桶之間大致均勻地分布哈希即可。)

如果我們的表容量n不是 2 的冪,則存儲桶的公式將是Math.abs(hash % n) ,它使用模運算符計算除以n后的余數,並使用abs來修復負值。 這會起作用,但會慢一些。

為什么慢? 想象一個十進制示例,其中您有一些隨機哈希值 12,459,217,並且任意表長度為 1,234。 12459217 % 1234恰好是 753 並不明顯。這是一個很長的除法。 但是,如果您的表格長度是 10 的精確,那么12459217 % 1000的結果就是最后 3 位數字:217。

binary編寫,2 的是 1 后跟一些 0,因此等效的技巧是可能的。 例如,如果容量n是十進制 16,即二進制 10000。因此, n - 1是二進制 1111,並且(n - 1) & hash僅保留與這些 1 對應的哈希的最后一位,其余的歸零。 這也將符號位清零,因此結果不能為負。 結果是從 0 到 n-1(含)。 那就是桶索引。

即使 CPU 變得更快並且它們的多媒體功能得到了改進,整數除法仍然是您可以執行的最昂貴的單指令操作之一。 它可能比按位 AND 慢 50 倍,並且在頻繁執行的循環中避免它可以帶來真正的改進。

我無法讀懂開發人員的想法,但我們這樣做是為了表明數字之間的關系。

比較一下:

int day = 86400;

對比

int day = 60 * 60 * 24; // 86400

第二個示例清楚地顯示了數字之間的關系,Java 足夠聰明,可以將其編譯為常量。

我認為原因是開發人員可以很容易地更改值(根據 JavaDoc '/* 默認初始容量 - 必須是 2 的冪。*/')例如1 << 51 << 3和他不需要做任何計算。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM