位操做 &、|、~、^、<<、>>

今天在看Java8的HashMap底層原理的時候看到多處操做二進制數的地方。雖然平時寫代碼不怎麼用到二進制的操做,建議之後還要往二進制操做上靠一靠,由於他確實能提升效率,並且是代碼更爲簡潔。在此,我在這裏說下它的定義和做用,不對的地方但願你們指正。java

位操做的定義

現實中的世界在計算機中都是以二進制0和1表示的,它的基數爲2,進位規則是:逢二進一,借位規則是:借一當二。數據在計算機中主要是以補碼的形式存儲的。下面咱們從HashMap源碼開始提及
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
複製代碼

取key的hashCode,各種型的hashCode實現方式不一樣,如Integer和String,代碼以下,此處咱們不針對hashCode()深究bash

// Integer
@Override
public int hashCode() {
    return Integer.hashCode(value);
}
public static int hashCode(int value) {
    return value;
}

// String
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}
複製代碼

左右移


(h >>> 16),將key的hashCode值,無符號右移16位。在此,說下右移>>和無符號右移>>>。
  • 右移>>:各二進位所有右移若干位,低位(移出的位數)丟棄,高位補符號位(符號位負補1),或者補零(符號位正補0)
    如8 >> 2,  8的二進制是0000 1000→右移兩位後是00 0010,由於8是整數→高位補兩個0獲得最終值是0000 0010 = 2(十進制)
  • 無符號右移>>>:各二進位所有右移若干位,高位補零,低位丟棄。無符號右移在java中只對32位和64位的值有意義
    如8 >>> 2,  在java中由於8是整形,佔4個字節,所以8(00000000 00000000 00000000 00001000)→右移兩位高位補0(00000000 00000000 00000000 00000010)結果是十進制2
    那-8 >>> 2的結果是多少呢?解析以下,上文咱們已經提到「數據在計算機中主要是以補碼的形式存儲的」,咱們都知道正數的補碼就是自己;負數的補碼是將其對應正數二進制表示全部位(除符號位)取反(0變1,1變0,符號位爲1不變)後加1。
    -8對應正數8(00000000 00000000 00000000 00001000)→全部位取反(11111111 11111111 11111111 11110111)→加1(11111111 11111111 11111111 11111000)結果是十進制1073741822
  • 左移<<:各二進位所有左移若干位,高位丟棄,低位補0
    如8 << 2,  8的二進制是0000 1000→左移兩位後是0010 00→低位補兩個0獲得最終值是0010 0000 = 32(十進制)
  • 無符號左移是不存在的,由於左右在低位補位,而低位沒有正負數的概念,所以不存在無符號左移

異或^

(h = key.hashCode()) ^ (h >>> 16) h與h無符號右移16位後進行異或計算。

運算規則:

兩個操做數對應的二進制位,相同爲0,不一樣爲1。如:0^0=0; 0^1=1; 1^0=1; 1^1=0

知足公式:(X^X=0 ,X^0=X)ide

交換律:A^B=B^Aui

結合律:A^B^C=A^(B^C)=(A^B)^Cspa

自反律:A^B^B=A^0=Acode

7^2                                     7^7                                         7^0
0111                                    0111                                        0111
0010                                    0111                                        0000
----                                    ----                                        ----
0101 = 5 (十進制)                       0000 = 0                                    0111 = 7
複製代碼

異或可在不引入第三個數的狀況下交換兩個數,以下代碼源碼

if (a != b) {  
    a ^= b;  
    b ^= a;  
    a ^= b;  
} 
複製代碼

按位與&

運算規則:

兩個操做數對應的二進制位,都爲1則爲1,不然爲0。如:1&1=1; 1&0=0; 0&1=0; 0&0=0
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
   ......
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
   ......
}
複製代碼
7&2                                     7&7                                         7&0
0111                                    0111                                        0111
0010                                    0111                                        0000
----                                    ----                                        ----
0010 = 2 (十進制)                       0111 = 7                                    0000 = 0
----

複製代碼

按位或|

運算規則:

兩個操做數對應的二進制位,有1則爲1,不然爲0。如:1|1=1; 1|0=1; 0|1=1; 0|0=0
static final int tableSizeFor(int cap) {
    int n = cap - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
複製代碼

在實例化HashMap時,在tableSizeFor(int cap)中使用了「|」,n |= n >>> 1;等價於n = n | (n >>> 1);方法的做用我會在接下來的HashMap源碼解析中提到。hash

按位非(按位取反)~

運算規則:

兩個操做數對應的二進制位,取反操做。如:1->0; 0->1
相關文章
相關標籤/搜索