Android通过位运算对多状态进行操作

提到位运算,我们都知道是**按位与(&)、按位或(|)、按位异或(^)、取反(~)**。并且,我们也知道每个运算符的作用,并且位运算的效率是很高的。但是,你在项目中用到过的有多少呢?(当然,可读性跟提升的这点效率之间还需要平衡。)。笔者对位运算也仅仅是知道一点,这也导致了,在处理问题的时候,并不会把它考虑在内。

在看Android源码的时候,发现源码里面有很多是通过位运算来解决问题的。比如,Activity中requestFuture(…)等等,发现位运算在解决存在多种状态的问题上,有很好的应用。

本文主要也是通过借鉴(copy)源码的一些方法,来实现状态的保存,删除,获取等。

这里先放出,完整的操作类。

public class QuickHandleStatus {
    private int mFuture;
    /**
     * 通过传入最大长度,来判断是否每个位置上都有数据
     */
    private int mFulFuture;

    public QuickHandleStatus() {
    }

    public QuickHandleStatus(int maxLength) {
        mFulFuture = (1 << maxLength) - 1;
    }

    /**
     * 添加状态
     */
    public void addFeature(int future) {
        final int flag = 1 << future;
        mFuture |= flag;
    }

    /**
     * 移除状态
     */
    public void removeFeature(int future) {
        final int flag = 1 << future;
        mFuture &= ~flag;
    }

    /**
     * 查询状态
     */
    public boolean containFeature(int future) {
        return (mFuture & (1 << future)) != 0;
    }
    /**
     * 根据,有参构造器传入的长度判断
     */
    public boolean isFull() {
        return (mFulFuture ^ mFuture) == 0;
    }

    public static void main(String[] args) {
        QuickHandleStatus status = new QuickHandleStatus(3);
        status.addFeature(0);
        status.addFeature(1);
        System.out.println(status.isFull());
    }
}

在查看具体的状态操作前,我们先来看下位运算操作符的基本使用。

运算操作符的基本使用

1、按位异或(^)

针对二进制的话,相同取0,不同取1.
比如说。7^15

  0111  7
^ 1111  15
--------
  1000

我们看到结果是8(1000)。

2、按位或(|)

针对二进制的话,有1的就取1,没1的就取0
比如说。2|4

  0010  2
| 0100  4
--------
  0110

我们看到结果是6(0110)。

3、按位与(&)

针对二进制的话,都是1的话取1,否则取0.
比如说。

  0010  2
& 0100  4
--------
  0000

结果是0.

在比如。

  1101  ~2
& 0110  6
--------
  0100

上面简单的说了下,位运算的基本操作。

下面,我们来尝试创建一个保存多状态的一个工具类

创建类。

先分析下,创建一个管理多状态的类,我们需要的东西。

首先,需要一个当前状态的标识,用int 类型来表示。它有32位,我们可以假设每一位表示一种状态。这样就满足了,存储多种状态的需求。
比如
1(0001),表示状态STATUS_ONE.
2(0010),表示状态STATUS_TWO.
3(0100),表示状态STATUS_THREE.

这样,把每一个int类型都当作一种状态。当我们知道哪一位上有值,就知道了当前的状态是哪种(或者哪些复合状态)了。比如,当当前状态的二进制最后一位是1的话(0001,xxx1),我们就知道,当前的状态是STATUS_ONE或者包含STATUS_ONE的状态。

我们传入的时候,也不想每次都是传入1<

然后,我们就是我们需要提供 添加状态,移除状态,查询状态的方法来操作状态的。

最后,我们就知道了我们工具类需要的东西:

  • 当前的状态情况,比如,用mFuture表示。
  • 添加状态,移除状态,查询状态的方法来供我们操作。

下面,我们就来具体实现。

public class QuickHandleStatus {
	//这个就是当前的状态
    private int mFuture;

    public void addFeature(int future) {
		//TODO: 添加状态
    }

    public void removeFeature(int future) {
		//TODO: 移除状态
    }

    public boolean containFeature(int future) {
		//TODO:判断当前状态是否有future的状态
    }
}

下面,我们就需要实现我们的方法,方便我们管理状态了。

先看下添加状态。

1、添加状态—addFeature(future)

上面我们分析**或运算(|)**的时候,我们知道,在二进制运算中,它的特性就是有1就取1,没有取0, 这种方式很适合,我们存储状态啊。

比如,上面类中的三种状态,我们每一个状态对应的数值不同,我们可以通过1向左位移的方式,比如1<< STATUS_ONE,来保证,每一个状态,只出现在int的二级制的一个位上(int类型有4个字节,32位,所以,要注意表示状态的数值的大小)。
我们在操作的时候,每个状态的值:

statusOne = 1 << STATUS_ONE = 1(0001);
statusTwo = 1 << STATUS_TWO = 2(0010);
statusThree = 1 << STATUS_THREE = 4(0100);
这样就解决了状态重复的问题,并且,每个状态都在int类型二级制的不同的位上。

让它跟当前状态取或(|),比如,当前状态(mFuture) = STATUS_TWO | 当前状态(mFuture)。如果mFuture最初状态是0的话,结果,mFuture的值肯定是 0010。因为,STATUS_TWO的第二位是1。

所以,添加方法就出来了。

    public void addFeature(int future) {
		//保证每个状态,都在int不同的位上
        final int flag = 1 << future;
		//通过|运算符,添加到当前状态mFuture上
        mFuture |= flag;
    }

removeFeature(…)

移除方法,我们该怎么做呢?
这里就用到2个运算符了。

首先 ,我们知道 **取反(~)**操作,就是二级制的每一位上都取相反的。
比如 STATUS_ONE(0001),这里只写后4位。取反的话 ~STATUS_ONE的值就是(1110)。

然后,就是**与运算(&)**操作,它的特性就是,如果,二进制的位上的值相同,就取相同的,不相同,就取0。上面已经说了
比如,0001 & 0011 = 0001 。

这样,我们就可以先把要移除的状态取反,这样就保证了,要移除的状态的那一位上是0,其他位上都是1;然后跟当前的状态取与(&)。这样就保证了,要移除的状态的那一位上肯定是0。其他位上跟之前没有变化。
不如,当前的状态是0101。要移除的状态是0001。
先取反,0001取反就是1110。(只写4位,其他一样)
再取与,1110 & 0101 = 0100。既保证了其他的位上的状态不变,只把当前要移除的状态移除。

最后的方法就出来啦

    public void removeFeature(int future) {
		//先获取当前状态的值
		final int flag = 1 << future;
		//通过上面的分析,先取反,后取与
		mFuture &= ~flag;
    }

最后一个查询状态

查询-containFeature(…)

我们在移除状态的时候,其实,已经包括了查询。就是先获取当前的状态值,然后,通过与操作(&)来获取值是否是0来判断(当前状态只有1位是1,其他位都是0)。取与的话,其他位肯定是0,如果当前位不是0的话,就说明当前状态中,那个位上是1.就是包括了,反之,就是不包括

    public boolean containFeature(int future) {
        return (mFuture & (1 << future)) != 0;
    }

到此,保存多状态操作的类就写完了。

使用场景?

  • 当登录时,用户名,密码都有数据时候,改变登录按钮颜色?
  • 当多线程执行,查看是否全部有返回结果的时候?
  • 当一个页面有多种状态,判断是哪种状态的时候?

当然,每个问题都有多种解决办法,这里只是提供了一个思路而已。

你可能感兴趣的:(技术,代码)