Java - 位运算的基本原理和用途
前言
Java 当中的位运算有很多种,它们都是针对二进制位进行操作。包括:
- 按位与:
&
- 按位或:
|
- 按位异或:
^
- 取反:
~
- 左移位、右移位运算符:
<< ,>> ,>>>
等
接下来我们就复习一下相关的运算知识。
一. Java 位运算基本操作
1.1 按位与 &
按位与:两个二进制数对应位上的数字都为1时,结果位上的数字才为1。否则结果位上的数字为0。按位与通常用于掩码操作或清零操作。
举例如下:
3的二进制表示为:0 0 1 1
5的二进制表示为:0 1 0 1
那么3 & 5 则表示为:
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1
最终结果为:
0011 & 0101 = 0001 = 1
按位与 常见的运用有:
- 判断一个数是否为奇数:
n & 1 ==1
代替 (n % 2 == 1
) - 清除一个数的二进制末尾
n
位:x & (~0 << n)
1.2 按位或 |
按位或:二进制数对应位上的数字有一个为1时,结果位上的数字就为1。
举例如下:
3的二进制表示为:0 0 1 1
5的二进制表示为:0 1 0 1
那么3 | 5 则表示为:
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
最终结果为:
0011 | 0101 = 0111 = 7
按位或 常用的运用有:
- 将一个数的二进制末
n
位设置为1:x | ((1 << n) - 1)
1.3 按位异或 ^
按位异或:两个二进制数对应位上的数字不相同时,结果位上的数字为1;否则结果位上的数字为0。
举例如下:
3的二进制表示为:0 0 1 1
5的二进制表示为:0 1 0 1
那么3 ^ 5 则表示为:
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
最终结果为:
0011 | 0101 = 0110 = 6
按位异或 常见的运用有:
- 加解密操作。
还有常见的运算公式:
x ^ 0 = x
x ^ 1 = ~x
1.4 按位取反 ~
按位取反:是指一个二进制数的每个位取反,即0变成1,1变成0。说简单点,对于一个二进制数,取反后的值也就是数值加1后取反数 : ~n = (n+1) * -1
举例如下:
3的二进制表示为:0000 0000 0000 0000 0000 0000 0000 0011
按位取反操作会将每一位取反,即将0变为1,将1变为0。所以,对3进行按位取反的结果为:
1111 1111 1111 1111 1111 1111 1111 1100
这个二进制数表示的是一个负数,根据补码的规则,我们需要将其转换为原码来得到对应的十进制数。转换原码的方法是将补码的每一位取反,然后再加1
对于
1111 1111 1111 1111 1111 1111 1111 1100
取反得到
0000 0000 0000 0000 0000 0000 0000 0011
再加1得到
0000 0000 0000 0000 0000 0000 0000 0100,
即十进制数-4
1.5 位移运算
位移运算分为:
- 左移运算符(
<<
) - 右移运算符(
>>
) - 无符号右移运算符(
>>>
)
注意:没有 <<<
这种运算符的哦~
1.5.1 左移运算符 <<
左移运算符:将操作数的二进制表示向左移动指定的位数,右侧用0填充。例如,a << b
表示将a
的二进制表示向左移动b
位。
举例如下:
3 << 2 = 12
3 的二进制表示为 0011
3 << 2 的运算过程如下:
0011 -> 1100
因此,3 << 2 = 12
说白了就是这个数乘以2的几次幂。
1.5.2 右移运算符 >>
右移运算符 :将操作数的二进制表示向右移动指定的位数,左侧用符号位(即正负号位)填充。例如,a >> b
表示将a
的二进制表示向右移动b
位。
举例如下:
-6 >> 1 = -3
-6 的二进制表示为
1111 1111 1111 1111 1111 1111 1111 1010
-6 >> 1 的运算过程如下:(关注后面的1010 --> 1101),101 整体向右移动了一位,左侧由1填充。
1111 1111 1111 1111 1111 1111 1111 1101
因此,-6 >> 1 = -3
说白了就是这个数除以2的几次幂。 (取整)
- 如果操作数是负数:右移操作会在左侧用1填充。
- 如果操作数是正数,右移操作会在左侧用0填充
1.5.3 无符号右移运算符 >>>
无符号右移运算符 :将操作数的二进制表示向右移动指定的位数,左侧用0填充。无符号右移运算符不考虑符号位,所以无论操作数是正数还是负数,都会将左侧的位数填充为0。例如,a >>> b
表示将a
的二进制表示向右移动b
位。
和右移运算符的区别就是:
- 无论是正数还是负数:右移操作会在左侧都用0填充。
例如:6 >>> 1 = 6 / 2¹ = 3
二. 位运算实际运用
2.1 判断奇偶性(&的运用)
一个数和1做按位与操作,返回结果是1代表奇数,否则偶数。
n & 1 == 1
2.2 交换两个数的值(^的运用)
将两个数字异或比较3次即可。
public static void main(String[] args) {
int a = 3, b = -7;
System.out.println("a= " + a + ",b= " + b);
a ^= b;
b ^= a;
a ^= b;
System.out.println("a= " + a + ",b= " + b);
}
结果如下:
2.3 2的幂运算(<< 的运用)
给定一个数,想要乘以2的几次m幂:例如3 * 2 的六次幂 = 192,可表示为:
3 << 6
2.4 判断一个数是否是2的幂次方(&的运用)
对于一个正整数 n
,如果它是 2 的幂次方,则有 n & (n - 1) == 0
。
2.5 加解密操作(^的运用)
private static final String TOKEN = "as21312b&*@#";
public static String encrypt(String str) {
char[] chars1 = str.toCharArray();
char[] chars2 = TOKEN.toCharArray();
StringBuilder builder = new StringBuilder();
for (int i = 0; i < chars1.length; i++) {
builder.append((char) (chars1[i] ^ chars2[i % chars2.length]));
}
return builder.toString();
}
public static String decrypt(String str) {
return encrypt(str);
}
public static void main(String[] args) {
String str = "Hello World";
// 加密
String encrypt = encrypt(str);
System.out.println(encrypt);
// 解密
System.out.println(decrypt(encrypt));
}
运行结果如下:
上面是一个简单的案例,你也可以在他的基础上,多加一层判断,比如:
- 防止二次加密。
- 没有加密的字符串经过解密后还是原字符串。