专栏首页一杯82年的JAVAJAVA中有趣的位运算

JAVA中有趣的位运算

&, |, ^, ~ 这些符号什么意思?有什么妙用?一起来感受它们的神奇吧~

当我们看一些源码的时候,经常会看到诸如 &、|、^、~ 的符号,这些就是位运算符。

位运算是直接对一个整形的二进制位进行操作,效率上比起加减乘除高不少,因此常运用在对性能很敏感的场景。

& 与运算

在二进制格式下,将两个数的每一位(1或0)分别做与运算(1&1=1,其它=0),得到一个新的二进制数。

public class Bit {
    public static void main(String[] args) {
        /*
         *   十进制       二进制
         *     5        0 1 0 1    从最低位(右)开始比较,不足的为0
         *     与          与
         *    14        1 1 1 0
         *     =           =
         *     4        0 1 0 0
         */
        System.out.println(5 & 14);
    }
}
// 输出: 4

判断整数n是奇数还是偶数:

  • n & 1 = 0 偶数
  • n & 1 = 1 奇数

原理:二进制格式下,右边第一位是0则是偶数,反之为奇数,因此只需要和1进行与运算即可。

| 或运算

在二进制格式下,将两个数的每一位(1或0)分别做或运算(0|0=0,其它=1),得到一个新的二进制数。

public class Bit {
    public static void main(String[] args) {
        /*
         *   十进制      二进制
         *     2        0 1 0     从最低位(右)开始比较,不足的为0
         *     或         或
         *     4        1 0 0
         *     =          =
         *     6        1 1 0
         */
        System.out.println(2 | 4);
    }
}
// 输出: 6

在Linux系统中,文件权限管理用1、2、4分别表示执行x、写w、读r的权限。

可以看做一个三位的二进制数,每一位分别表示一种权限的开启与否(1开启,0关闭),通过或运算组合就得到了不同的权限组合。

所以最高权限就是7,即二进制的“111”,拥有读、写、执行全部权限。而777权限则是所属用户、组用户、其他用户都拥有最高权限。

基于这个思路,我们只需要一个int或者long型的数字就可以存储几十个布尔类型的属性值,在某些场景下很有用。

^ 异或运算

异或:相同为false,不同true

在二进制格式下,将两个数的每一位(1或0)分别做异或运算(0^0=0,1^1=0, 其它=1),得到一个新的二进制数。

public class Bit {

    public static void main(String[] args) {
        /*
         *   十进制      二进制
         *     2        0 1 0     从最低位(右)开始比较,不足的为0
         *    异或       异或
         *     6        1 1 0
         *     =          =
         *     4        1 0 0
         */
        System.out.println(2 ^ 6);
    }
}
// 输出: 4

异或有个有趣的特性,它的逆运算是它本身,即A^B=C,C^B=A。基于这个特点,可以做一个简单的加密,把B作为秘钥,原文A用秘钥B加密后进行传输或存储等,使用时再用秘钥B进行解密。

通过异或操作还能实现两个数的交换,不需要中间值。(简单测了下性能并没有很棒棒)

public class Bit {

    public static void main(String[] args) {
        int x = 2;// 010
        int y = 4;// 100
        x = x ^ y;// 110
        y = y ^ x;// 010
        x = x ^ y;// 100
        System.out.println("x = " + x);
        System.out.println("y = " + y);
    }
}

/* 输出:

x = 4
y = 2

*/

~ 非运算

在二进制格式下,将两个数的每一位(1或0)分别做非运算(~0=1,~1=0),得到一个新的二进制数。

public class Bit {

    public static void main(String[] args) {
        System.out.println(~1);
    }

}
// 输出: -2

1进行非运算后值成了负数,不只是1,只要是正数,取非后都是负数,因为对于有符号的整数,最高位(最左边)是用来表示正负的,最高位为0是正数,1是负数。

正数1非运算后从“00000000000000000000000000000001”变成了“11111111111111111111111111111110”。

二进制表示负数的情况,要转成十进制需要两个步骤:

  1. 逐位取反 -> 00000000000000000000000000000001(2进制)
  2. 加1 -> 00000000000000000000000000000010(2进制) -> 2(10进制)
  3. 加上负号 -> -2(10进制)

总结

通过位运算可以巧妙且高效地达到某些目的,但如果不是很有必要,并不建议使用,毕竟可读性不高,别人看起来太痛苦(想想在阅读源码时看到一堆位运算的心情)。

这次简单介绍了与、或、非、异或,下次再讲讲移位操作的实践。

本文分享自微信公众号 - 一杯82年的JAVA(acupjava),作者:acupt

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-30

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 跨域Access-Control-Allow-Origin解决方案

    本地启动了一个web服务,地址为 127.0.0.1:8882 ,然后通过一个本地静态页面去请求这个接口。虽然在同一台电脑,但依然是跨域的。

    acupt
  • 探索JAVA并发 - 如何减少锁的竞争

    所谓可伸缩性,即当增加计算资源(如CPU、内存、带宽等)时,程序的吞吐量或处理能力会相应增加。这个时候,我们当然希望增加的效果越明显越好,不过如果锁竞争太严重,...

    acupt
  • 探索JAVA并发 - 如何优雅地取消线程任务

    一种常用的方法是在任务代码中加入一个“是否取消”的标志,任务定期去查看这个标志是否改变,如果被改变了就取消剩下的任务,此时如果想取消这个任务只需要修改它的标志,...

    acupt
  • 位运算

    用户3004328
  • Head First设计模式——蝇量模式和解释器模式

    在一个设计房子的平台中,周围要加上一些树,树有一个坐标XY坐标位置,而且可以根据树的年龄动态将自己绘制出来。如果我们创建许多树之后,会有许多树的实例对象。使用一...

    SpringSun
  • 设计模式之 装饰器模式

    tanoak
  • .NET基础拾遗(2)面向对象的实现和异常的处理基础

      在C#中申明一个类型时,只支持单继承(即继承一个父类),但支持实现多个接口(Java也是如此)。像C++可能会支持同时继承自多个父类,但.NET的设计小组认...

    Edison Zhou
  • SpringBoot自定义

    崔笑颜
  • 在C#中使用依赖注入-工厂模式和工厂方法模式

    工厂模式和工厂方法模式是设计模式中较为常见的两种模式,借助于依赖注入可以更好的发挥模式的特性。本文将通过一个业务需求的变化过程来阐述如何更好的使用设计模式与依赖...

    newbe36524
  • spring boot使用JDBCTemplate访问Mysql

    根据个人喜好选择配置文件的类型,在这里我选择配置application.yml,主要对datasource进行一些配置说明。

    create17

扫码关注云+社区

领取腾讯云代金券