Java移位运算符

Posted x_k

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java移位运算符相关的知识,希望对你有一定的参考价值。

前言

以前看到过移位运算符,基本没怎么用过,也就没怎么留意,无意间看到工具类的一个方法用到了,java.util.Collections.binarySearch(),下面是详细代码:

public static <T>
    int binarySearch(List<? extends Comparable<? super T>> list, T key) 
        if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
            return Collections.indexedBinarySearch(list, key);
        else
            return Collections.iteratorBinarySearch(list, key);
    

    private static <T>
    int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) 
        int low = 0;
        int high = list.size()-1;

        while (low <= high) 
            int mid = (low + high) >>> 1;
            Comparable<? super T> midVal = list.get(mid);
            int cmp = midVal.compareTo(key);

            if (cmp < 0)
                low = mid + 1;
            else if (cmp > 0)
                high = mid - 1;
            else
                return mid; // key found
        
        return -(low + 1);  // key not found
    

可以看到该方法调用下面的静态私有方法中使用了移位运算符">>>"。

原理

在网上搜索了一番,看到有的博主说移位运算符适用于的数据类型,如:byte、char、short、int、long。下图为测试:

原理:在计算机中,数据都是以二进制形式存储的,移位运算符就是通过二进制来实现数据变化。

右移运算符>>

正数

一个int类型的数15,二进制数为
0000 0000 0000 0000 0000 0000 0000 1111(15)
使用右移运算符>>1,得到二进制数为
0000 0000 0000 0000 0000 0000 0000 0111(7)
使用右移运算符>>2,得到二进制数为
0000 0000 0000 0000 0000 0000 0000 0011(3)

二进制数向右移,左边补0,即:去掉最右端,在左端补0

负数

一个int类型的负数:-15,二进制数为
1000 0000 0000 0000 0000 0000 0000 1111(-15)
使用右移运算符>>1,过程为:
1.符号不移动,其他向右移动一位
1000 0000 0000 0000 0000 0000 0000 0111
2.末位加1
1000 0000 0000 0000 0000 0000 0000 1000(-8)

二进制数向右移,符号位不变,其他与正数相同,移动完毕,末位加1
注意:不要将位移运算(>>1)当做除以2或者乘以2,负数不成立

测试截图:

左移运算符<<

正数

一个int类型的数15,二进制数为
0000 0000 0000 0000 0000 0000 0000 1111(15)
使用左移运算符<<1,得到二进制数为
0000 0000 0000 0000 0000 0000 0001 1110(30)
使用右移运算符<<2,得到二进制数为
0000 0000 0000 0000 0000 0000 0011 1100(60)
二进制数向左移,右边补0,即:去掉最左端,在右端补0

负数

一个int类型的负数-15,二进制数为
0000 0000 0000 0000 0000 0000 0000 1111(-15)
使用左移运算符<<1,过程为:
1.符号不移动
1000 0000 0000 0000 0000 0000 0001 1110
2.左移,右边补0
1000 0000 0000 0000 0000 0000 0001 1110(-30)
二进制数向左移,符号位不变,其他与正数相同,移动完毕,末位补0

测试截图:

无符号右移运算符>>>

正数

以int类型数7为例,二进制数为:
0000 0000 0000 0000 0000 0000 0000 0111(7)
使用无符号,右移运算符>>>1,二进制数为:
0000 0000 0000 0000 0000 0000 0000 0011(3)
基本与右移运算符相同,二进制数向右移,左边补0,即:去掉最右端,在左端补0

负数

以int类型数-7为例,二进制数为:
1000 0000 0000 0000 0000 0000 0000 0111(-7)
使用无符号,右移运算符>>>1
1.计算出反码(正数的反码与其源码相同;负数的反码与其源码逐位取反,但符号位除外)
1111 1111 1111 1111 1111 1111 1111 1000
2.右移,左边补0
0111 1111 1111 1111 1111 1111 1111 1100(2147483644)
先计算反码,然后右移,最后左边补0

测试截图:

总结

以上计算方式是我自己整理的,没有在书籍中看到过,如果有误,希望指正。
回到最初的代码中,使用的无符号右移运算符:

int mid = (low + high) >>> 1;

我猜这行代码的作用和除以2相差无几,但是效率应该更高。最后,稍微提一下,觉得这个工具类的二分查找方法的API文档有点问题:

the index of the search key, if it is contained in the list; 
otherwise, (-(insertion point) - 1). 
The insertion point is defined as the point at 
which the key would be inserted into the list: 
the index of the first element greater than the key, or list.size() 
if all elements in the list are less than the specified key. 
Note that this guarantees that the return 
value will be >= 0 if and only if the key is found.

java.util.Collections.binarySearch(List<? extends Comparable<? super T>>, T)

个人觉得otherwise, (-(insertion point) + 1)

#附:测试代码

package com.edu;

import java.util.Arrays;
import java.util.List;

/**
 * @author xukai
 * @date 2017年3月9日 下午6:39:55
 * Collections.binarySearch
 */
public class Test0309 

    public static void main(String[] args) 
        List<Integer> list = Arrays.asList(2,4,7,10,11,45,50,59,60,66);
        System.out.println("get 9: " + indexedBinarySearch(list, 9));
        
        testType();
        testRight();
        testLeft();
        testNot();
    
    
    private static <T>
    int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) 
        int low = 0;
        int high = list.size()-1;

        while (low <= high) 
            int mid = (low + high) >>> 1;
            Comparable<? super T> midVal = list.get(mid);
            int cmp = midVal.compareTo(key);

            if (cmp < 0)
                low = mid + 1;
            else if (cmp > 0)
                high = mid - 1;
            else
                return mid; // key found
        
        return -(low + 1);  // key not found
    
    
    public static void testType() 
        byte b = 0;
        char c = (int)0;
        short s = 0;
        int i = 0;
        long l = 0;
        System.out.println("byte-0: " + (b >> 2));
        System.out.println("char-0: " + (c >> 2));
        System.out.println("short-0: " + (s >> 2));
        System.out.println("int-0: " + (i >> 2));
        System.out.println("long-0: " + (l >> 2));
    
    
    public static void testRight() 
        System.out.println((int)15 >> 1);
        System.out.println((int)15 >> 2);
        System.out.println((int)-15 >> 1);
        System.out.println((int)-15 >> 2);
        System.out.println((int)-15 / 2);
    
    
    public static void testLeft() 
        System.out.println((int)15 << 1);
        System.out.println((int)15 << 2);
        System.out.println((int)-15 << 1);
    
    
    public static void testNot() 
        System.out.println((int)7 >>> 1);
        System.out.println((int)-7 >>> 1);
        System.out.println((int)-1 >>> 1);
    



以上是关于Java移位运算符的主要内容,如果未能解决你的问题,请参考以下文章

java中的无符号移位运算

Java移位运算之算术右移位

一篇文章搞懂移位运算

java位运算

Java中的位运算

韩顺平 java笔记 第20讲 二进制 位运算 移位运算