Java中位操作,以及在日常编码中的使用

机器中是如何存储的

背景:在计算机中,有原码、反码、补码三种形式,首先先介绍一下原码:
所谓的原码:表达简单明了,是人类最容易理解的表示法;
但是,原码有一个很大的弊端,使用原码进行计算的时候非常复杂,特别是两个操作数符号不一样的时候。步骤如下:
1.判断两个数绝对值的大小 2.使用绝对值大的数减去绝对值小的数 3.对于符号数,以绝对值大的为准。
所以,需要一种使用加法来替代减法的操作,从而消除减法。为了解决这个问题补码出现了;在计算机中,存储数据是使用补码来进行存储的。
那么 : 原码 反码 补码 之间的关系是怎么样的呢?
对于正数来说:原码=反码=补码
对于负数来说:反码等于原码按位取反,补码等于反码+1 在源码变反码的过程中,符号位是不变的。

测试:
5 + -5
5的补码: 00000000000000000000000000000101
-5的补码:111111111111111111111111111111111011
它们相加的话: 00000000000000000000000000000000 第一个进位的1会溢出,所以,答案为0;

案例 通过补码来计算原码
有一个需求,Java写的程序和Modbus通信,因为Java中,byte的范围是[-128, 127)的,所以,赋值0xff是错误的;
我们都知道0xff 代表的是 1111 1111 那么,java中什么数代表的是这个值呢?
我们已经知道计算机为了计算方便,使用的是补码存储。我们需要计算出原码,来确定这个值; 1111 1111 这个代表的是补码;很显然,反码 + 1 = 补码;所以,我们可以先求出反码; 反码 = 1111 1110;然后,我们在算出原码,除了符号位其他取反 原码 = 1000 0001 所以,这个值为-1;

按位取反
比如说:~5 那么它的答案是多少呢?称这个值为某值。
5的补码: 00000000000000000000000000000101
~5 : 111111111111111111111111111111111010
这个结果是存在机器中的补码,还是属于补码,是某值的补码(反码+1 = 补码)
某值的补码: 111111111111111111111111111111111010
某值的反码: 111111111111111111111111111111111001
由于求反码是不对符号位进行取反的,所以,某值的源码为
某值的原码: 10000000000000000000000000000110;
答案为-6
在这里插入图片描述
按位与
& : 当两位同时为1时,才返回1
按位或
| :当有一位为1时,才返回1
按位异或
^ : 两位相同时,返回0,两位不同时,返回1
左移运算
<< : 将二进制码整体向左移动指定位数,左边的舍去,右边空出来的位以0填充。
右移运算
>> : 这个是有符号右移运算,左边空出来的位以原来的符号位填充,右边的舍去。
>>> : 这个是无符号位右移运算,左边空出来的位总是以0填充。

上面了解了一下,在计算机中,整形的存储格式,下面来了解一下Java位运算的经典应用:

一、判断整数的奇偶性

按照传统的思路,我们判断奇偶性都是通过用这个数与2取摸,看运算结果是否为0。学了位运算以后,我们可以换一种思路来思考问题:任何一个偶数,其原码都为0;如果是正数的话,其补码和原码一样;如果是负数的话;反码加1最终结果也是为0;
-2原码 : 1xxxx010
-2反码 : 1xxxx101
-2补码 : 1xxxx110
所以,负数的补码,最后一位也是为0的。
这样,我们就可以通过和1与(&)操作来进行奇数偶数的判断;
在这里插入图片描述
备注:需要加一个括号;

二、找出单身狗元素

但是狗元素是指:一个数组中,某个数只出现了一次,而其他数都出现了两次,要求把这个出现一次的数找出来。完成这个题目核心原理,**就是两个相同的数字进行异或运算,结果为0。如果,一个数与0进行异或运算,结果就是这个数。**代码如下:
在这里插入图片描述
结果为3,解释一下原理,这里4 1 4进行异或的话,虽然,4 和1 先进行异或,等于5,5再和4进行异或,结果为1。根据这个原理,最终是会找出这个3的。
升级版1
这里的意思是,只要两个数通过异或就等于0,所以,如果一个数出现3次,其他数出现两次,也是可以找到这个数的。
在这里插入图片描述
结果为4。
升级版2
有两个数组,a b 其中,b中包含a中所有的数,并且,b中比a中多一个数。
其实一样的,把 a b中所有的数进行异或,就可以得到最终解了。
在这里插入图片描述

三、加密操作

通过,异或操作可以实现加密;比如说,有一个值为a, a ^ b = c 如果只是知道c的值,是无法知道a的值是多少的。但是,如果我们知道b的值的话,可以通过c ^ b = a来推出a的值。
字符串加密: 理解了加密和解密的最基本的原理以后,我们再来说说,如何具体对字符串实施加密操作。我们知道,位运算只能对byte、short、int、long和char这几种基本数据类型进行运算,对字符串这种引用数据类型并不支持。但是,我们可以给其转换为byte类型的数据在进行加密;

    public static void main(String[] args) throws UnsupportedEncodingException {
        String s = "小卢同学";
        byte[] bytes = s.getBytes("utf-8");
        byte key = 100;
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) (bytes[i] ^ key);
        }
        System.out.println("加密后结果:" + new String(bytes));
        for (int i = 0; i < bytes.length; i++) {
            bytes[i] = (byte) (bytes[i] ^ key);
        }
        System.out.println("解密后结果:" + new String(bytes));
    }

结果符合预期:
在这里插入图片描述

四、判断某数是不是2的N次幂

我们知道2的一次幂的二进制是10;而1的二进制是01; 4的二进制是0100 3的二进制是 0011
所以,我们可以得到一个结论,如果说,这个数是a > 0,如果a和a-1进行与操作,发现为0,那么这个数就是一个2的n次幂;
在这里插入图片描述

五、求一个集合的所有子集

注意:如果不含重复元素的,那么所有子集也不会重复,如果是含有重复元素的,那么还是使用回溯比较好。
在力扣78题,有一个相似的问题
在这里插入图片描述
很显然,这个nums[]中有三个元素,那么就有2的3次方种情况。
[000] [001] [010] [011] [100] [101] [110] [111] 其中1代表选上该数,0代表不选该数;
所以,现在我们需要的就是判断,把这个1和nums[]数组中的数,进行匹配。
如果与1与不等于0 说明,可以选择nums[0],
如果与(1<<1)与不等于0 说明可以选择nums[1];
如果与(1<<2)与不等于0 说明可以选择nums[2];

public List<List<Integer>> subsets(int[] nums) {
        ArrayList<List<Integer>> res = new ArrayList<>();
        int length = nums.length;
        if (length == 0){
            return res;
        }

        // i 代表的是二进制数
        for (int i = 0; i < (1<<length); i++) {
            ArrayList<Integer> childList = new ArrayList<>();
            // j代表要左移的位数 j能左移的范围是[0,length - 1]
            for (int j = 0; j < i && j < length; j++) {
                // 判断下标为j的元素是否出现在子集中
                if ((i&(1<<j)) != 0){
                    childList.add(nums[j]);
                }
            }
            res.add(childList);
        }
        return res;
    }

在这里插入图片描述
这里说明一下:为什么j<i才执行,因为i代表的是二进制数,如果它小于等于j,比如说001 = 1,2左移一位肯定大于i了,和其与也不会等于1了。
为什么小于length 以三个数为例 最大为111 而(1<<3)为1000 他们不可能有相交的数。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值