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

今天在看Java8的HashMap底层原理的时候看到多处操作二进制数的地方。虽然平时写代码不怎么用到二进制的操作,建议以后还要往二进制操作上靠一靠,因为他确实能提高效率,而且是代码更为简洁。在此,我在这里说下它的定义和作用,不对的地方希望大家指正。

位操作的定义

现实中的世界在计算机中都是以二进制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()深究

// 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)

交换律:A^B=B^A

结合律:A^B^C=A^(B^C)=(A^B)^C

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

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源码解析中提到。

按位非(按位取反)~

运算规则:

两个操作数对应的二进制位,取反操作。如:1->0; 0->1
  • 6
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值