深入理解位运算以及位运算在Java源码中的运用

本文详细介绍了位运算在Java中的概念、七种基本位运算符及其应用,包括判断奇偶数、交换变量值、改变符号、数据处理等,同时展示了位运算在ByteBuffer和基础数学运算中的实际运用。
摘要由CSDN通过智能技术生成

一、什么是位运算

程序中的数在计算机中都是以二进制形式存储,位运算就是直接对整数在内存中的二进制位进行操作,位运算 包括位逻辑运算和移位运算,位逻辑运算能够方便设置或屏蔽某个字节的一位或几位,也可以对两个数按位相加,移位运算可以对内存中某个二进制左移或右移几位

二、Java中提供的七种位运算

2.1、位运算核心操作

位运算符

 名称

 含义

 举例

 &

按位与

将参与运算的两个二进制数进行&与运算,如果两个二进制位都是1,则与运算的结果为1,其他全都为0。(0与任意数N&运算都是0)

a&b

 |

按位或

将参与运算的两个二进制数进行|或运算,两个二进制位只要其中1个是1 ,那么就是1,如果2个二进制位都是0则表示0。(0与任意数N|运算都是任意数N)

a|b 

按位异或

将参与运算的两个二进制数进行^异或运算,如果2个二进制位都是0或者都是1,那么就是0,如果两个二进制位不同,则为1。任何数与0异或运算都是其本身,任何数与自己进行异或运算结果都是0

 a^b

按位非

一元操作符,按位取反。每个二进制位上都取相反值,1变成0,0变成1。

 ~a

<< 

左移

将一个数各二进制位全部向左移动若干位,左移运算没有有符号和无符号左移动,在左移时,移除高位的同时在低位补0

a<<2 

>> 

右移

又称为有符号右移。将一个数各二进制位全部向右移动若干位,若参与运算的数字为正数,则在高位补0;若为负数,则在高位补1。

a>>2 

>>> 

无符号右移 

将一个数各二进制位全部向右移动若干位,右移之后左边都是补上0。忽略符号位 

 a>>>2

  位运算优先级从高到低依次为:取反(~)、左移位(<<)、右移位(>>)、无符号右一(>>>)、位与(&)、位异或(^)、位或(|)

2.2、位运算扩展操作

&= 按位与赋值

|= 按位或赋值

^=按位非赋值

<<= 赋值左移

>>=右移赋值

>>>=无符号右移赋值

四、位运算的应用

1、判断奇偶数

if(n&1==1){

//n是个奇数

}

其核心就是判断二进制最后一位是否为1,若是,则结果加上2^0=1,一定是奇数

2、交换两个数

a = a ^ b;

b = a^b  //b = (a^b)^b=a^0 = a

a = a^b // a = (a^b)^a = b

3、变化符号

通过对数值X取反并加1来改变其符号

x = ~x + 1;

4、清除最低位的1

使用 x & (x - 1) 可以将最低位的 1 清零,这个技巧可以用于计算二进制中 1 的数量或者确定一个数是否为2的幂

5、得到最低位的1

使用 x & (-x) 可以提取出 x 的最低位的 1。

6、设置和清除特定位

设置(Set)位:要设置 x 的第 n 位为 1,可以用 x |= (1 << n)。

清除(Clear)位:要清除 x 的第 n 位,可以用 x &= ~(1 << n)。

7、位运算在ByteBuffer源码中的应用

static int getIntB(ByteBuffer bb, int bi) {
        return makeInt(bb._get(bi    ),
                       bb._get(bi + 1),
                       bb._get(bi + 2),
                       bb._get(bi + 3));
    }

// -- get/put int --

    static private int makeInt(byte b3, byte b2, byte b1, byte b0) {
        
        return (((b3) << 24) |
                ((b2 & 0xff) << 16) |
                ((b1 & 0xff) <<  8) |
                ((b0 & 0xff)      ));
    }

//(b3) << 24)将 b3 左移 24 位。由于 int 类型是 32 位的,因此将 b3 左移 24 位可以将 b3 放到结果的最高位(最高有效字节位置)。

// b2 & 0xff:这一步是为了确保 b2 只取其最低的 8 位。因为 byte 类型是有符号的,如果 b2 的最高位是 1,则整数会被视为负数,这不是我们想要的。通过 & 0xff 操作,可以将 b2 转换为无符号整数,并且只保留其最低的 8 位。

// << 16:将 b2 左移 16 位,将其放置到结果的第二个字节的位置。

// b1 & 0xff:同样的操作,确保 b1 只取其最低的 8 位。
// << 8:将 b1 左移 8 位,将其放置到结果的第三个字节的位置。
// b0 & 0xff:同样的操作,确保 b0 只取其最低的 8 位。

// 最后一步不需要左移,因为 b0 将放置在结果的最低字节的位置。

这段代码实现了将四个字节按照大端字节序(Big Endian)组合成一个整数的功能。在这个函数中,b3 表示最高有效字节(Most Significant Byte,MSB),b0 表示最低有效字节(Least Significant Byte,LSB)

通过按照大端字节序的顺序组合字节,确保了生成的整数的高位包含了原始字节序列中的高位字节,低位包含了原始字节序列中的低位字节。

8、位运算实现加减乘除

1)加法

加法可以通过迭代使用异或(^)来计算无进位和,与(&)然后左移来计算进位,然后将它们加在一起,直到没有进位为止。

public class BitwiseOperations {

    public int add(int a, int b) {

        while (b != 0) {

            int sum = a ^ b; // 无进位和

            int carry = (a & b) << 1; // 进位

            a = sum;

            b = carry;

        }

        return a;

    }

}

2)减法

减法可以通过取补码来实现。要从a中减去b,我们可以添加b的补码。补码可以通过取反(~)再加1得到

add函数在二进制加法中已经实现

public int subtract(int a, int b) {

    // 加上b的补码(即 -b)

    return add(a, add(~b, 1));

}

3)乘法

乘法可以通过位移和加法来实现。我们对乘数进行迭代,如果当前位为1,则将被乘数加到结果上,然后被乘数左移一位。

public int multiply(int a, int b) {

    int result = 0;

    while (b != 0) {

        if ((b & 1) != 0) {

            result = add(result, a);

        }

        a <<= 1;

        b >>>= 1; // 使用逻辑右移,对于负数也适用

    }

    return result;

}

4)除法

除法是乘法的逆运算。我们可以找到商,通过不断从被除数中减去除数的倍数来实现。

public int divide(int dividend, int divisor) {

    if (divisor == 0) {

        throw new ArithmeticException("Cannot divide by zero");

    }

    if (dividend == Integer.MIN_VALUE && divisor == -1) {

        // 特殊情况处理,避免溢出

        return Integer.MAX_VALUE;

    }

   

    int result = 0;

    int sign = (dividend < 0) ^ (divisor < 0) ? -1 : 1; // 判断结果的符号

   

    // 将被除数和除数都转换为正数

    dividend = Math.abs(dividend);

    divisor = Math.abs(divisor);

   

    while (dividend >= divisor) {

        int tempDivisor = divisor;

        int quotient = 1;

        while ((tempDivisor << 1) <= dividend) {

            tempDivisor <<= 1;

            quotient <<= 1;

        }

       

        dividend -= tempDivisor;

        result = add(result, quotient);

    }

   

    return sign == -1 ? subtract(0, result) : result;

}

在所有这些函数中,我们必须处理溢出的情况,尤其是当涉及到最小整数值的时候。除法中有一个特殊情况,当最小值除以-1时,会产生溢出,因为其绝对值比Integer.MAX_VALUE大1,所以需要特殊处理。

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值