敞开成长之旅!这是我参与「日新方案 2 月更文应战」的第 19 天,点击检查活动详情

前语

位运算其实是一个比较常用的操作,有的人或许说,并没有啊,我如同基本就没怎样用过位运算,但假如你经常看源码,你就会发现源码里边有很多位运算的操作,并且这些操作我个人觉得确实是有很意思,所以位运算很重要,平时用不多,也需求经常去回忆温习。

由于暂时写的,有些源码的操作我不太记住是出自哪里了,假如今后碰到了,会在谈论区进行弥补。比方会有一些取反核算再取反回来的操作,比方左移和右移,我现在不太记住是出自哪里了,反正比较经典的。我的个人或许就记住用位运算来表明状况,由于我会经常用这个。

位运算根底

简略来回忆一下根底的核算,位运算会分为一元运算和二元运算。

一元有左移<<,右移>>,无符号右移动>>>和取反~

左移是是什么?比方 0010 左移动一位就变成 0100 (注意这里是二进制的表明),右移动便是 0100 变成 0010。 当然没这么简略啦,二进制也有表明正负值的标志位,右移之后,左面会补标志位的数。而无符号右移动是左面补0。也便是说正数的右移和无符号右移动的结果相同,而负数就不同了。这样说应该好了解吧?那思考一下为什么没有无符号左移

仍是不好懂,不要紧,咱们讲慢些,假如8转成2进制是00001000
而-8便是先取反码,反码便是取反,所以8的反码是11110111,然后再用反码取补码,补码便是+1,所以这里得到补码1111000。即-8转成二进制是11111000
先看看咱们对11111000取反码,得00000111,再取补码得00001000,看到从-8到8也是同样的操作。

然后咱们看右移一位和无符号右移一位的效果。
-8的二进制11111000,右移一位是11111100,这是多少啊?咱们能够用上面的核算看它的正数是多少,反码00000011,补码00000100,这是4吧,所以11111100是-4。
同理看无符号右移,得01111100,反码10000011,补码10000100,这是多少?2的7次方+4,所以是不同的。

取反就好了解了,取反便是逻辑非,比方0010取反便是1101

二元有与&&、或||、异或^

这些都好了解吧,与运算 1010 && 1001 = 1000 , 或运算 1010 || 1001 = 1010 , 异或 1010 ^ 1001 = 0011 ,这没什么好讲的,很根底。

位运算很简略?

一看,哎哟,真的简略,就这?1分钟学会了。学会?那会用吗?

不要紧,咱们来看一题算法:

一个整型数组里除了两个数字只呈现一次,其他的数字都呈现了两次。请写程序找出这两个只呈现一次的数字。例如:

输入:

[1,4,1,6]

返回值:

[4,6]

明确说了,这题是用位运算,可是怎样做?

开发中位运算的运用

这个我由于暂时写的,android源码里确实是有些比较经典的位运算运用场景,我现在不完全记住,只能先列举几个,其它的假如今后想起来,会在谈论区中做弥补。

用来表明状况

位运算能够用来表明状况,我之前也写过一篇文章 /post/715547… 差不多也是这个意思。

比方window的flags,看看它的定义

public static final int FLAG_LAYOUT_IN_SCREEN   = 0x00000100;
public static final int FLAG_LAYOUT_NO_LIMITS   = 0x00000200;
public static final int FLAG_FULLSCREEN      = 0x00000400;
public static final int FLAG_FORCE_NOT_FULLSCREEN   = 0x00000800;
......

那他这样做有什么优点,他能一个变量表明多个维度的状况。比方FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_NO_LIMITS|FLAG_FULLSCREEN|FLAG_FORCE_NOT_FULLSCREEN便是 1111 (二进制表明)

假如你要判别这个window是不是同时设置了这4个flag,要怎样判别,就直接if(flags == 15)啊,多简略

可是假如你用多个变量存flag要怎样判别, if(isScreen && isNoLimits && usFullscreen && isForceNot),这样写就很难看,很不方便,我window的flag多着呢,莫非你要排火车?

数组扩容

来看看ArrayList的扩容源码

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

这里的int newCapacity = oldCapacity + (oldCapacity >> 1);便是阔人操作,这个右移是什么?看不懂也不要紧,自己套一个数字进去算就知道了,是除2吧,那ArrayList的扩容便是旧容量的一半。

看着简略是吧?那有没有想过一个问题,他写这个代码,为什么不写oldCapacity/2,而写oldCapacity >> 1

那既然右移一位 >> 1 是除2,那左移一位 << 1 是什么操作呢?是什么核算呢?

总结

我这里确实是好久没有用位运算,所以需求温习一下。这个东西对开发来说很重要,比方你是开发应用层的,你觉得这个是底层用到的,你用不到,并不是这样。就拿那个表明状况的来说,自从我看到源码用这一招之后,只需有适宜的场景,我也会这样用。

不管是做数学运算,仍是逻辑运算,位运算都能适用,它是很简略就能学会,可是学会用,那便是别的一回事,当然不是说看完我这篇文章就开端瞎用,能在适宜的场合去运用,那效果十分的好,用不上也不要紧,至少要有个意识,这样不管在看源码仍是其它时分,都是能帮到你的。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。