关于java:二进制究竟有什么用齐姐带你看看那些好玩儿的位操作

45次阅读

共计 2905 个字符,预计需要花费 8 分钟才能阅读完成。

本篇终于讲到了齐姐文章里经常呈现的分割线!

计算机说到底就是 0 和 1,所有的数在内存中都是以二进制的模式贮存的。

而位操作,或者说位运算,就是间接对内存中的二进制位进行操作。

位运算能够说是咱们的基本功,明天这篇文章就从以下角度和大家一起玩转位运算。

  1. 二进制到底有什么用?
  2. 原码 反码 补码
  3. 7 种位运算

当然了,位运算还有很多奇技淫巧,如果大家还想看进阶篇,记得给我 点赞 或者 留言 通知我哦~

二进制的作用

在理论生产中,二进制是用来 优化工夫和空间 的。

二进制的运算,可能并不会升高复杂度的等级,然而能够把复杂度后面的系数降下来。

举个例子。

大家都晓得堆,或者叫优先队列,一般来说是用 齐全二叉树 来实现的,叫做 二叉堆

最小堆

二叉堆插入、删除元素的工夫复杂度都是 O(logn),如果这个不分明的同学连忙在公众号内回复「」温习一下,或者点击这里~

然而有另一种堆,它可能做到 O(1) 的工夫插入元素,O(logn) 的工夫删除元素,我在堆这篇文章里也提到过,就是 斐波那契堆

但为什么不必呢?

就是因为 O(1) 后面的系数十分大。

咱们说 O(logn)O(1) 好,是有个条件的,那就是 n 十分十分大的状况下,然而实际上,如果 n 是在 int 范畴内,那么取个 log 也不过就是 32 了,反而这个 O(1) 的工夫复杂度可能系数达到几百几千。

一般来说理论利用中工夫的测量并不是工夫复杂度这么简略,有的时候就须要你把两个算法都实现进去,去跑去测量它的工夫,能力决定哪个好。

那么二进制一次可能作用于 32 位上(假如是一个 int),如果数据表示的奇妙,这齐全能够优化 32 倍,多用几个 int 就多优化了好几个 32 倍,不香吗?

除了优化工夫,还能够优化空间。

比方在网站公布新版本时,个别都会附上反对该版本的浏览器列表,不然有些老掉牙的浏览器看不到我的新性能还算我的锅么?

那么怎么无效的示意这个浏览器列表呢?

全世界所有浏览器都有个国际标准编号,这里我就简略假如一下:

  • 0 示意 QQ 浏览器
  • 1 示意 Chrome 浏览器
  • 2 示意火狐浏览器
  • 3 示意 …

那么咱们就能够用一个 int 示意是否反对 32 个浏览器的状态,如果这个浏览器能用,那么这一位上就设为 1,那么比方国内的某个网站能够示意为:

  • 0b …. 1101

所以位操作在很多代码里都很罕用,比方网络协议、操作系统等等。

接下来咱们说说具体的知识点。

原码 反码 补码

数字有正有负,Java 中用的是 signed type,就是有正有负的。

尽管在 Java 8 之后,也用了个工具来实现 unsigned type,然而其实底层实现是没有的。

二进制最右边的一位是符号位,

  • 0 示意这个数是非正数;
  • 1 示意这个数是正数。

对了,最右边的一位英文叫做 most significant bit,好多同学面试说的形形色色。。。

负数

负数的原码反码补码雷同,没啥好说的。

比方:

  • int 1 = 0b 0000 0000 0000 0001
  • int 2 = 0b 0000 0000 0000 0010

正数:

原码:把相应的负数的符号位设为 1。

  • -1 的原码 = 0b 1000 0000 0000 0001
  • -2 的原码 = 0b 1000 0000 0000 0010

反码 ones’ complement:符号位是 1,其余位取反。

  • -1 的反码 = 0b 1111 1111 1111 1110
  • -2 的反码 = 0b 1111 1111 1111 1101

补码 two’s complement:反码 + 1。

  • -1 的补码 = 0b 1111 1111 1111 1111
  • -2 的补码 = 0b 1111 1111 1111 1110

而计算机中真正用来存储数据的是用 补码

这里略微留神下反码和补码的英文,ones' 的这个 ' 在前面,two's 的这个 ' 在两头。。

为什么计算机要用补码来存储数据呢?

可能有同学会说正零负零的起因,但这只是表面现象。

实际上通过补码这样精美的设计,计算机做加减乘除运算就不必思考符号,就能够让硬件里 CPU 的设计变得异样简略。

最后计算机只有加法器没有减法器,所以它用这么一种形式用加法实现了减法。

int 的最大值是多少?

正是因为最右边一位是符号位,所以负数的示意就少了一位能用的,那么 int 的最大值就是:

0111111…11 (31 ones) = 2^31 – 1 = 2147483647

7 种位运算

运算符

中文

英文

运算规定

<<

左移

left shift

左边补充 0

>>

右移

signed right shift

右边补充符号位

>>>

无符号右移

unsighed right shift

Java 特有,右边补充 0

~

位非

NOT

每位取反

&

位与

bitwise AND

每位做与操作,都是 1 则为 1,否则为 0

I

位或

OR

每一位做或操作,有 1 则为 1,否则为 0

^

异或

XOR

雷同为 0,不同为 1

要留神的是前 4 个运算符是对 1 个数进行操作的,且操作实现后这个数自身的值不变;后 3 个操作是两个数的运算。

咱们一一来看。

为了书写不便,上面的数值尽管是 int 类型,但我只写 8 位,大家都能了解的噢!

1. <<

左移操作就是把这些零啊壹啊的整体往左挪动 n 位,左边缺的就补充 0。

  • 1 = 0b 0000 0001
  • 1 << 1 = 0b 0000 0010 = 2
  • 2 = 0b 0000 0010
  • 2 << 1 = 0b 0000 0100 = 4
  • 3 = 0b 0000 0011
  • 3 << 1 = 0b 0000 0110 = 6

诶,大家发现没有,左移 1 位之后这个数相当于 乘 2

然而这只实用于右边溢出的高位中 不蕴含 1 时。

如果把 1 扔了,那就必定不是 2 倍了嘛。

2. >>

  • 1 = 0b 0000 0001
  • 1 >> 1 = 0b 0000 0000 = 0
  • 2 = 0b 0000 0010
  • 2 >> 1 = 0b 0000 0001 = 1
  • 3 = 0b 0000 0011
  • 3 >> 1 = 0b 0000 0001 = 1

同理,右移操作的成果是这个数 除以 2

如果是正数呢?

  • -3 = 1111 1101
  • -3 >> 1 = 1111 1110 = -2

因为 Java 是向零取整,所以 奇数 时会有问题,就不再是除以 2 的后果。

总结一下,

  • 对于非正数、正数且是偶数,右移一位与除以 2 后果一样;
  • 对于正数且是奇数,右移一位不等于除以 2。

3. >>>

和 >> 的不同之处在于,这个的右边不管正负,一律补充 0。

所以对于负数来说,和 >> 的成果一样,然而正数不同。

  • -3 = 1111 1101
  • -3 >> 1 = 01111 1110 = 很大的数。。

4. ~

取反操作,就是每一位取反,1 变成 0,0 变成 1。

  • 3 = 0b 0000 0011
  • ~3 = 0b 1111 1100

5. &

这个符号其实和逻辑与运算 && 意思一样,只不过作用在每一位上。

对于每一位来说,两个数都是真,则为真,否则为假。

  • 3 = 0b 0000 0011
  • 5 = 0b 0000 0101
  • 3&5 = 0b 0000 0001

6. |

同理,和逻辑或运算 || 意思一样,只不过作用在每一位上。

对于每一位来说,凡是有个真的就是真,否则为假。

  • 3 = 0b 0000 0011
  • 5 = 0b 0000 0101
  • 3|5 = 0b 0000 0111

7. ^

最初一个异或操作,雷同为 0,不同为 1。

  • 3 = 0b 0000 0011
  • 5 = 0b 0000 0101
  • 3^5 = 0b 0000 0110

对了,本周末新建了国内读者交换群,想退出的小伙伴后盾回复「进群」拉你进群呀~

另外 8 月自习室流动最初一周了,给咱们自习室的小伙伴打起,应该有不少小伙伴能拿到齐姐的红包了,还没学够 21 天的要持续加油呀!

9 月的自习室正在筹备中,如果你想加入,通知我 9 月你想学习的天数和每天学习的时长,咱们一起学习抱富!~

正文完
 0