有趣的位运算-移位运算

1左移、右移、无符号右移

左移(<<):运算符左边的对象向左移动运算符右边指定的位数(在低位补0)

右移(>>):运算符左边的对象向右移动运算符右边指定的位数。使用符号扩展机制(如果值为正,则在高位补0,如果值为负,则在高位补1)

无符号右移(>>>):运算符左边的对象向右移动运算符右边指定的位数。采用0扩展机制(无论值的正负,都在高位补0)

注:x<<y 相当于 x*2;x>>y相当于x/2y
    从计算速度上讲,移位运算要比算术运算快。
    如果x是负数,那么x>>>3没有什么算术意义,只有逻辑意义。(计算机是补码的形式存储)

与按位运算符一样,移位运算符可以用于byte、short、int、long等整数类型,和字符串类型char,但是不能用于浮点数类型float、double;当然,在Java5.0及以上版本中,移位运算符还可用于byte、short、int、long、char对应的包装器类。

2有趣的问题

对一个int型的数字a,a>>32 = a<<32 = a。

例如,整数1(0000 0000 0000 0001),按照上述规则1<<32 = 0(0000 0000 0000 0000),左移低位补0。而实际结果却是1.

Think in Java给出了解释:

“对char,byte或者short进行移位处理,那么在移位进行之前,它们会自动转换成一个int。只有右侧的5个低位才会有用。这样可防止我们在一个int数里移动不切实际的位数。若对一个long值进行处理,最后得到的结果也是long。此时只会用到右侧的6个低位,防止移动超过long值里现成的位数。”

说白了就是移多了没什么意义。这里5个低位[00000,11111]。所以以上几个类型只有在[0,31]范围位移才有效。那么若位移数大于31会怎么样呢?

jdk是这么做的:采用模运算。所以1<<32  = 1<<(32%32)= 1<<0 = 1。

3位移运算在hashMap源码中的经典应用

    static int hash(int h) {  
        h ^= (h >>> 20) ^ (h >>> 12);  
        return h ^ (h >>> 7) ^ (h >>> 4);  
    }  

之前看源码的时候一直不明白为什么要这么做。下面大家看一个例子可能就猜出来了(结合hashMap设计原则)

public class Test {

    public static void main(String[] args) {
        Integer number1 = 80000;
        printInfo(number1.hashCode());
        printInfo(hash(number1.hashCode()));
        
        Integer number2 = 1024;
        printInfo(number2.hashCode());
        printInfo(hash(number2.hashCode()));
    }
    
    
    //  输出一个int的二进制数
    private static void printInfo(int num){
        System.out.println(Integer.toBinaryString(num));
    }
    
    static int hash(int h) {  
        h ^= (h >>> 20) ^ (h >>> 12);  
        return h ^ (h >>> 7) ^ (h >>> 4);  
    }  

}

输出:

10011100010000000
10010100101101011
10000000000
10001001000

hashMap采用数组和链表实现,设计当然希望这个HashMap里面的 元素位置尽量的分布均匀些,尽量使得每个位置上的元素数量只有一个,那么当我们用hash算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,而不用再去遍历链表,这样就大大优化了查询的效率。而原始数据经过hash方法后,1的分布更加均匀。有没有想到点什么?不得不佩服设计这段代码的大神~

原文地址:https://www.cnblogs.com/ouym/p/8808671.html