位运算及其常用操作

Java位运算

位运算符分为逻辑运算符(~、|、&、^)和移位运算符(<<、>>、>>>)。位运算操作的是二进制的数。

逻辑运算符
1、^(亦或运算) ,针对二进制,相同的为0,不同的为1。

2、&(与运算) 针对二进制,只要有一个为0,就为0

3、| 两个位只要有一个为1,那么结果就是1,否则就为0

4、~ 取反

5、原码 原码就是符号位加上数字的二进制表示。

6、反码

一个数如果为正,则它的反码与原码相同;一个数如果为负,则符号位为1,(符号位不变化,其余位数取反)。

换言之 该数的绝对值取反(绝对值取反各位都取反)。

为了简单起见,我们用1个字节来表示一个整数:

+7的反码为:00000111

-7的反码为: 11111000

7、 补码

补码:一个数如果为正,则它的原码、反码、补码相同;一个数如果为负,取到反码然后加1。(反码加1就是补码)为了简单起见,我们用1个字节来表示一个整数:

+7的补码为: 00000111

-7的补码为: 11111001

移位运算符
1、<<:左移,将运算符左边的运算对象向左移动右侧指定的位数。注意,是二进制表示的时候,相当于运算符左侧的运算对象乘以2(在不溢出的情况下),二进制形式最低位补0。

2、>>:右移运算符,将运算符右边的运算对象向右移动指定的位数。相当于将运算符左边的数除以2,二进制形式对应的最高位补上符号位。(负数的二进制表示形式为其补码)

3、>>>:无符号右移运算符,和右移运算一样,但是二进制表示时最高位补上时无论被操作数是正是负,补上的都是0。

注意
若对char,byte或者short进行移位处理,那么在移位进行之前,先将它们转化成int。 只有右侧的5个低位才会用到。这样可防止我们在一个int数里移动不切实际的位数。 若对一个long值进行处理,最后得到的结果也是long。此时只会用到右侧的6个低位,防止移动超过long值里现成的位数。 但在进行“无符号”右移位时,也可能遇到一个问题。若对byte或short值进行右移位运算,得到的可能不是正确的结果,它们会自动转换成int类型,并进行右移位。但“零扩展”不会发生,所以在那些情况下会得到-1的结果。
看个简单的例子

package com.demo;

public class Test {
    public static void main(String[] args){
        System.out.println(new Integer(5).toBinaryString(5));
        System.out.println(new Integer(-5).toBinaryString(-5));
        System.out.println(new Integer(5>>1).toBinaryString(5>>1));
        System.out.println(new Integer(-5>>1).toBinaryString(-5>>1));
        System.out.println(new Integer(5>>>1).toBinaryString(5>>>1));
        System.out.println(new Integer((-5)>>>1).toBinaryString((-5)>>>1));
    }
}

运行结果

101
11111111111111111111111111111011
10
11111111111111111111111111111101
10
1111111111111111111111111111101  //共31位,最前面的0略去了

说明:位运算的效率高于乘除运算,因为计算机底层就是0 1二进制来处理的。

位运算优先级

~的优先级最高,其次是<<、>>和>>>,再次是&,然后是^,优先级最低的是|。

位运算总的来说比较简单,但是应用却是很多,要能熟练掌握并使用常用的应用。

应用一:判断奇偶数

a&1 == 0;则a为偶数
a&1 == 1;则a为奇数

说明: 1的二进制表示形式除了最后一位其余所有的位都是0,所以a&1对应的除了最后一位全部是0,我们知道,一个数如果是偶数,则最后一位二进制位肯定是0,因为其他位都是2的整数次方,而如果一个数是奇数,则最后一位二进制位肯定是1。所以,当a&1 == 0时,说明a是偶数,当a&1 == 1时,说明a是奇数。

应用二:交换两数

int a = 5;
int b = 9;
a ^= b;
b ^= a;
a ^= b;

说明:直接看第二步,其实就是这样:b = b^ (a^b) = a;

因为异或运算满足交换律,一个数亦或自己结果为0,而任意一个数亦或0都等于自身,所以b = b^ (a^ b) = b^ b^ a = 0^a = a。

再来看第三步,a = a^ b;此时,a = (a^ b)^a;(第一个a为原来的a,值为5;第二个a代表的是第二步运算,其实是交换后的b的值,这里的b也是指的是为交换之前的b),所以,a = b;(b为交换之前的b),所以两数就会发生交换。

应用三:求整数的绝对值

int i = a>>31;
System.out.println(i == 0? a : (~a + 1));

说明:对于一个整数a,a>>31,若a为正数,a>>31的值为0,若a为负数,a>>31的值为-1。当 i 的值为0时,也就是当a为正数的时候,直接就输出a本身;当 i 的值为-1时,说明a为负数,输出负数的反码 + 1,也就是这个数的绝对值。

应用四:变换符号

int a = 45;
int b = -9;
System.out.println(~a + 1);
System.out.println(~b + 1);

输出结果为-45 9,即变换了正负号。

应用四:求两个数的平均值
两个数相加有时候可能相加之后的值超出了对应类型的范围,当然可以采用大数进行处理,但是还有一种方法可以处理,

(x&y)+((x^y)>>1);
如下

package com.demo;

public class WeiYunSuan {
    public static void main(String[] args){
        System.out.println(Integer.valueOf("11111111111111110010110",2));
        System.out.println(Integer.valueOf("11111111111111010001111",2));
        System.out.println((8388502 + 8388239)/2);
        int a = 8388502; //a对应的十进制
        int b = 8388239; //b对应的十进制
        System.out.println((a&b) + (a^b)>>1);
        System.out.println(Integer.valueOf("11111111111111100010010",2));

    }
}
/// a   11111111111111110010110
/// b   11111111111111010001111
/// a&b 11111111111111 01000 0110
/// a^b 00000000000000 100011001
/// a^b>>1 1000 1100
/// a&b + a^b>>1  11111111111111
/// 010000110
///  10001100
/// 11111111111111100010010

执行结果:

8388502
8388239
8388370
4194255
8388370

说明了可以采用这种方法进行两个数平均值的求解

应用五:取余运算

对于 a%b,当b为2的n次方的时候 a%b等价于a&(b-1)

应用六:取int型变量的第k位

a>>(k-1)&1;运算顺序为先右移k-1位,在与1

应用七:从x位到y位共有多少个1

package com.demo;

public class WeiYunSuan {
    public static void main(String[] args){
        int a = 23;
        int sum = 0;
        System.out.println(new Integer(a).toBinaryString(a));
        for(int i = 1; i <= 4; i++)
            sum += a>>(i-1)&1;
        System.out.println(sum);
    }
}

结果:

10111
3

应用八:取末k位
a&((1<<k) - 1),当结果长度不够时,应该在最左端加上相应的0就可以了

总结

功能                示例         位运算
去掉最后一位    (101101->10110) x >> 1
在最后加一个0    (101101->1011010)   x < < 1
在最后加一个1    (101101->1011011)   x < < 1+1
把最后一位变成1    (101100->101101)    x | 1
把最后一位变成0    (101101->101100)    x | 1-1
最后一位取反    (101101->101100)    x ^ 1
把右数第k位变成1    (101001->101101,k=3)    x | (1 < < (k-1))
把右数第k位变成0    (101101->101001,k=3)    x & ~ (1 < < (k-1))
右数第k位取反    (101001->101101,k=3)    x ^ (1 < < (k-1))
取末三位    (1101101->101)  x & 7
取末k位    (1101101->1101,k=5) x & ((1 < < k)-1)
取右数第k位    (1101101->1,k=4)    x >> (k-1) & 1
把末k位变成1    (101001->101111,k=4)    x | (1 < < k-1)
末k位取反    (101001->100110,k=4)    x ^ (1 < < k-1)
把右边连续的1变成0    (100101111->100100000)  x & (x+1)
把右起第一个0变成1    (100101111->100111111)  x | (x+1)
把右边连续的0变成1    (11011000->11011111)    x | (x-1)
取右边连续的1    (100101111->1111)   (x ^ (x+1)) >> 1
去掉右起第一个1的左边    (100101000->1000)   x & (x ^ (x-1))
判断奇数        (x&1)==1
判断偶数        (x&1)==0
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值