主要总结下java的位运算符的操作。java的位运算符不紧可以提高运行效率,同时也有会意想不到的效果(java.util.ArrayDeque有很好的体现),在后续中会举例说明。
开始说位运算符之前,先简单的复习下补码的知识,然后举个简单的例子说明下计算机中的补码操作(计算机中的加减法)。
计算机进行加减法操作时,都是以补码进行操作的,所以java中的位运算都是以补码进行操作的。正数的补码是其本身,而负数的补码为其反码+1。如下例子。
因为java中int型是4个字节,需要32位,为了写起来和看起来更加直观,这里使用8位来进行模拟。
对于正数2 ,其二进制形式为00000010,其补码也为00000010。
对于负数-2,其二进制形式为10000010,其补码为11111110。
为什么计算机会用补码进行操作,我用十进制来模拟一下。举两个数,一个是a=9145,一个是b=0645。(第一位为符号位,9表示负数,0表示正数)
对于二进制来说,1取反为0,因为1+0=1(比2小1);对于十进制来说,1取反为8,因为1+8=9(比10小1);对于16进制来说,1取反为14,因为1+14=15(比16小1);
所以对于十进制a=9155,他的反码为9844,而其补码为9845(反码+1),对于b,因为是正数,所以补码跟原码一样。所以a+b(补码相加)=9845+0645=10500。因为我们都是四位正数进行相加,所以10500的第一位就要舍去,得到的结果为0500。而0代表的是正数,所以结果就是500;
再举一个例子 比如 a=9645,b=0145(第一位为符号位,9表示负数,0表示正数)
a的补码为9355,b的补码跟原码一样。a+b=9355+0145=9500。所以结果就是-500。
计算机为什么要用补码运算呢,而不是用原码,因为计算机不像人一样,他不会进行减法运算,只是通过一种方式将减法变成了加法运算,也就是所谓的补码。
介绍完了补码,接下来介绍java的位操作(通过补码来进行操作)。
5>>1,-5>>1; 结果分别为2,-3;
先分析5>>1,因为5的补码为00000101。右移一位以后为00000010(结果为补码),再求其原码(跟补码一样),结果为2。因为5是正数,所以最高位补0,
再分析-5>>1,因为-5的补码为11111011。右移一位以后为11111101(结果为补码),在求其原码,结果为10000011,转换成十进制为-3。因为-5是负数,所以最高位补1。
5>>>1,-5>>>1;结果分别为2,2147483645
在例1右移操作中,正数移动时,最高位补0,而负数移动时,最高位补1,而对于无符号右移,不管是正数还是负数,移动时最高位都会补0;
5>>>1和例1中类似,直接分析-5>>>1。-5的补码为11111011,无符号右移一位后为01111101,再求其原码(移动后变成了正数,所以原码和补码一致)最后结果为2147483645。
public ArrayDeque(int numElements) {
allocateElements(numElements);
}
private void allocateElements(int numElements) {
int initialCapacity = MIN_INITIAL_CAPACITY;
// Find the best power of two to hold elements.
// Tests "<=" because arrays aren't kept full.
if (numElements >= initialCapacity) {
initialCapacity = numElements;
initialCapacity |= (initialCapacity >>> 1);
initialCapacity |= (initialCapacity >>> 2);
initialCapacity |= (initialCapacity >>> 4);
initialCapacity |= (initialCapacity >>> 8);
initialCapacity |= (initialCapacity >>> 16);
initialCapacity++;
if (initialCapacity < 0) // Too many elements, must back off
initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
}
elements = (E[]) new Object[initialCapacity];
}
public boolean add(E e) {
addLast(e);
return true;
}
public void addLast(E e) {
if (e == null)
throw new NullPointerException();
elements[tail] = e;
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity(); //这个方法在后续的博客中讲解,这里先不用管
}
public E pop() {
return removeFirst();
}
public E removeFirst() {
E x = pollFirst();
if (x == null)
throw new NoSuchElementException();
return x;
}
(tail = (tail + 1) & (elements.length - 1)
doubleCapacity();
如果队列一直不满的话。此时的数组就会不断循环使用。这些操作都是通过位操作来完成的。