1.常见的位运算使用场景
位运算是按照整数的二进制位进行移位、与、或、非、异或的运算,应用于整数类型(int),长整型(long),短整型(short),字符型(char),和字节型(byte)等数据类型(浮点型不能进行位算)。由于,位运算直接采用二进制进行计算,所以往往可以得到计算性能的提升。
1.1.移位运算 << 或 >>
5.<<(左移):各二进制位全部左移n位,左移翻倍。
//<<运算:各二进制位全部左移n位。左移翻倍
int n1 = 2;//2的二进制数为:0010
int n2 = 3;//3的二进制数为:0011
int ret1 = n1<<2;//n1的二进制数(0010)向左移动2位:1000
int ret2 = n2<<1;//n1的二进制数(0011)向左移动1位:0110
System.out.println(ret1);//所以ret1结果为:8(十进制)
System.out.println(ret2);//所以ret2结果为:6(十进制)
6.>>(右移):各二进制位全部右移n位,右移减半。
//>>运算:各二进制位全部右移n位。右移减半
int n1 = 8;//2的二进制数为:1000
int n2 = 6;//3的二进制数为:0110
int ret1 = n1>>2;//n1的二进制数(1000)向左移动2位:0010
int ret2 = n2>>1;//n1的二进制数(0110)向左移动1位:0011
System.out.println(ret1);//所以ret1结果为:2(十进制)
System.out.println(ret2);//所以ret2结果为:3(十进制)
1.2.与运算 &
&与运算的规则是:必须两个数同时为1,结果才为1
public class Main {
public static void main(String[] args) {
int i = 167776589; // 00001010 00000000 00010001 01001101
int n = 167776512; // 00001010 00000000 00010001 00000000
int x = i & n; // 00001010 00000000 00010001 00000000
System.out.println(x); // 167776512
}
}
1.3.或运算 |
|或运算的规则是:只要任意一个为1,结果就为1
public class Main {
public static void main(String[] args) {
int i = 167776589; // 00001010 00000000 00010001 01001101
int n = 167776512; // 00001010 00000000 00010001 00000000
int x = i | n; // 00001010 00000000 00010001 01001101
System.out.println(x); // 167776589
}
}
1.4.非运算 ~
~非运算的规则是:0和1互换(反转)
public class Hello {
public static void main(String[] args) {
int i = 167776589; // 00001010 00000000 00010001 01001101
int x = ~i; // 11110101 11111111 11101110 10110010
System.out.println(x); // -167776590
}
}
1.5.异或运算 ^
^异或运算的规则是:如果两个数不同,结果为1,否则为0。
public class Hello {
public static void main(String[] args) {
int i = 167776589; // 00001010 00000000 00010001 01001101
int n = 167776512; // 00001010 00000000 00010001 00000000
int x = i ^ n; // 00000000 00000000 00000000 01001101
System.out.println(x); // 77
}
}
1.6.常见用途
- 移位运算,计算指定数值n的50%:n >> 1
- 移位运算,计算指定数值n的2倍:n << 1
- 与运算,判断奇偶数 a&1 == 0 偶数 a&1 == 1 奇数
- 求平均值,防止溢出 (x&y)+((x^y)>>1)
- 异或运算,交换两个整数
2.整数类型运算时的类型溢出问题,产生原因以及解决办法
2.1溢出原因
由于整数存在范围限制,如果计算结果超出了范围,就会产生溢出,而溢出不会出错,会得到一个奇怪的结果。
例如
public class Main {
public static void main(String[] args) {
int x = 2147483640;
int y = 15;
int sum = x + y;
System.out.println(sum); // -2147483641
}
}
解释上述结果:把整数2147483640和15换成二进制做加法,观察一下运算过程。
0111 1111 1111 1111 1111 1111 1111 1000
+ 0000 0000 0000 0000 0000 0000 0000 1111
-----------------------------------------
1000 0000 0000 0000 0000 0000 0000 0111
由于最高位计算结果为1,因此,加法结果变成了一个负数。
2.3溢出解决方法
解决上面的问题,可以把int换成long类型,由于long可表示的整型范围更大,所以结果就不会溢出:
long x = 2147483640;
long y = 15;
long sum = x + y;
System.out.println(sum); // 2147483655
整形基本数据类型的范围
- byte:-128~+127
- short:-32768~+32767
- int:-2147483648~+2147483647(-2^31~+2^31-1)
- long:-2^63~+2^63-1
2.4用BigInteger表示任意大小的整数
如果我们使用的整数范围超过了long型,就要用数据结构来模拟一个大整数。java.math.BigInteger就是用来表示任意大小的整数。BigInteger内部用一个int[]数组来模拟一个非常大的整数:
BigInteger bi = new BigInteger("1234567890");
// 2867971860299718107233761438093672048294900000
System.out.println(bi.pow(5));
对做运算的时候,只能使用实例方法,例如,加法运算:
BigInteger i1 = new BigInteger("1234567890");
BigInteger i2 = new BigInteger("12345678901234567890");
BigInteger sum = i1.add(i2); // 12345678902469135780
BigInteger和long型整数运算比,BigInteger不会有范围限制,但缺点是速度比较慢。也可以把BigInteger转换成long型:
BigInteger i = new BigInteger("123456789000");
System.out.println(i.longValue()); // 123456789000
System.out.println(i.multiply(i).longValueExact()); // java.lang.ArithmeticException: BigInteger out of long range
使用longValueExact()方法时,如果超出了long型的范围,会抛出ArithmeticException。
BigInteger和Integer、Long一样,也是不可变类,并且也继承自Number类。因为Number定义了转换为基本类型的几个方法:
- 转换为byte:byteValue()
- 转换为short:shortValue()
- 转换为int:intValue()
- 转换为long:longValue()
- 转换为float:floatValue()
- 转换为double:doubleValue()
2.4.1BigInteger小结
- BigInteger用于表示任意大小的整数
- BigInteger是不变类,并且继承自Number父类
- BigInteger转换成基本类型时可使用longValueExact()等方法保证结果准确,超出范围溢出时,会抛出ArithmeticException异常