理解Java-位运算

位运算符主要针对二进制,它包括了:“与”、“非”、“或”、“异或”。

数据基本单位

在了解运算符前,先了解一下数据的基本单位

  • BIT(比特): 一个二进制位(最小数据单位), 比如:0
  • BYTE(字节): 1BYTE = 8BIT, 比如:01010110
  • KB(千字节): 1KB = 1024BYTE
  • MB(兆字节): 1MB = 1024KB
  • GB(吉字节): 1GB = 1024MB
  • TB(太字节): 1TB=1024GB

Java中的基本数据类型

不同语言中的数据长度可能不一样, 这里介绍一下Java语言中的基础数据长度

数据类型与对应长度

类型 长度
boolean -
char 16bit
byte 8bit
short 16bit
int 32bit
long 64bit
float 32bit
double 64bit

程序示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
public void test(){
// 输出: 1
System.out.println(Byte.BYTES);
// 输出: 2
System.out.println(Short.BYTES);
// 输出: 4
System.out.println(Integer.BYTES);
// 输出: 8
System.out.println(Long.BYTES);
// 输出: 4
System.out.println(Float.BYTES);
// 输出: 8
System.out.println(Double.BYTES);
// 输出: 11111111111111111111111111111110
System.out.println(Integer.toBinaryString(-2));
// 输出: 1111111111111111111111111111111111111111111111111111111111111110
System.out.println(Long.toBinaryString(-2L));
char c = '苏';
// 输出: 苏
System.out.println(c);
}

^(异或)

参与运算的两个数, 如果两个相应位相同(二进制),则结果为0,否则为1.
即:0^0=0, 1^0=1, 0^1=1, 1^1=0

运算说明

操作 二进制 十进制
- 000000000000000000000000000000001 1
- 000000000000000000000000000000010 2
^ 000000000000000000000000000000011 3

程序测试

1
2
3
4
5
@Test
public void test(){
// 输出: 3
System.out.println(1^2);
}

&(与)

参与运算的两个数, 如果两个相应位都为1(二进制),结果才为1,否则结果为0.
即:0^0=0, 1^0=1, 0^1=1, 1^1=1

运算说明

操作 二进制 十进制
- 000000000000000000000000000000001 1
- 000000000000000000000000000000010 2
& 000000000000000000000000000000000 0

程序测试

1
2
3
4
5
@Test
public void test(){
// 输出: 0
System.out.println(1&2);
}

| (或)

参与运算的两个数, 如果两个相应位只要有一个为1(二进制),那么结果就是1,否则就为0.
即:0^0=0, 1^0=1, 0^1=1, 1^1=1

运算说明

操作 二进制 十进制
- 000000000000000000000000000000001 1
- 000000000000000000000000000000010 2
000000000000000000000000000000011 3

程序测试

1
2
3
4
5
@Test
public void test(){
// 输出: 3
System.out.println(1|2);
}

~(非)

参数计算的数,如果位为0(二进制),结果是1,如果位为1,结果是0.

运算说明

二进制 十进制 运算 二进制 十进制
000000000000000000000000000000001 1 ~ 11111111111111111111111111111110 -2
000000000000000000000000000000010 2 ~ 11111111111111111111111111111101 -3

程序测试

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void test(){

// 输出: -2
System.out.println(~1);
// 输出: 11111111111111111111111111111110
System.out.println(Integer.toBinaryString(~1));

// 输出: -3
System.out.println(~2);
// 输出: 11111111111111111111111111111101
System.out.println(Integer.toBinaryString(~2));
}

关于位运算的面试题

实现两个数的交换

思路: 两个数做异或,会计算出来一个中间数(即便这两个数相同,那么计算结果为0也满足),用这个中间数做交换时的中间引用停留即可.

代码实现

1
2
3
4
5
6
7
8
9
@Test
public void find(){
int a = 1, b=2;
System.out.println("交换前: a = " + a + ", b = " + b);
a = a ^ b;
b = a ^ b; // ((a^b) ^ b) = a
a = a ^ b; // ((a^b) ^ b) = a
System.out.println("交换后: a = " + a + ", b = " + b);
}

实现字符串翻转

思路: 使用异或进行高低位转换

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* @param str 待返转的字符串
* @return 翻转后的字符串
*/
public static String reverse(String str){
char[] chars = str.toCharArray();
int low = 0;
int top = chars.length - 1;
while (low < top){
chars[low] ^= chars[top];
chars[top] ^= chars[low];
chars[low] ^= chars[top];
low++;
top--;
}
return new String(chars);
}

一堆数, 除过一个数只出现了一次, 其它数均出现了2n次(n>=1), 找出该数.

分析: 因为相同的两个数做^结果为0, 0^任何数=任何数.
将这堆数从第一个开始,一直到最后一个进行异或运算, 那么最后结果值就是那个只出现了一次的数.

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
@Test
public void findOne(){
int[] arr = new int[] {1, 1, 2, 2, 3, 5, 5, 7, 7, 6, 6};
// 从第一个数标开始
int result = arr[0];
// 以此进行后面的数据的异或运算
for(int i = 1; i < arr.length; i++){
result = result ^ arr[i];
}
// 程序输出: 3
System.out.println(result);
}

java中|||,&&&有什么区别

&(与): 位运算符,也兼有逻辑预算符的功能.
&&(短路与): 只是逻辑运算符. &与&&作为逻辑运算符时有如下区别:

  • &:无论&左边是否为false,他都会继续检验右边的boolean值。
  • &&:只要检测到左边值为false时, 就会直接判断结果,不会在检验右边的值(因为”与”有一个false最后结果就是false了),所以&&的执行效率更高,所以逻辑运算时一般都是使用&&.

|(或)与||(短路或)区别同&(与)和&&(短路与)相似.