单一字段存储多状态思路
最近在做一个类似百度知道类的系统,针对每个回答以及问题都有可能有多重组合状态:
- 是否过期
- 是否重复
- 是否优质
- …
这些状态有个共同的特点,就是都是 Yes Or Not,如果拍平,每个状态都用一个独立的字段进行存储,字段存储信息过于简单先不说,将来如果增加状态的话,数据库结构也需要同步修改。
正在研究怎么处理的时候,感谢同事的提点,给我了一个单一字段存储这些值的思路。经过实践后,发觉用的挺好,于是总结一下。
基本原理
首先我们已知这些状态都是 Yes Or Not,且互斥的关系,不相交。因此我们可以将这些状态都看做是一个二进制数的一位,例如过期看做是 0001,重复是 0010,优质是 0100,这样,如果数据库中字段值是 3=(0b0011),就表示既过期,又重复。
这样如果我们将来状态不止这几种,只需要扩充二进制的位数即可。
计算
我们思路有了,如何提取结果就是目前的重要问题。
即:我给一个数据库值 3=(0b0011),如何反推出是优质和过期。
这就涉及到三个按位操作符——&、|、^。
&(按位与)
逐位进行与运算
A | B | 结果 |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
例如: | ||
0011 & 0001 = 0001 | ||
0010 & 0001 = 0000 |
|(按位或)
逐位进行与运算
A | B | 结果 |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
例如: | ||
0011 | 0001 = 0011 | |
0010 | 0001 = 0011 |
^(按位异或)
逐位进行与运算,相同为 0,不相同为 1
A | B | 结果 |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
例如: | ||
0011 ^ 0001 = 0010 | ||
0010 & 0001 = 0000 |
状态值计算
有个这三个工具后,就可以进行相应值的计算了
首先**计算状态组合,用|运算**,
例如:
一个问题是重复,不过期,不优质,就用 0001 | 0000 | 0000 = 0001 = 1(int)
一个问题是重复,过期,不优质,就用 0001 | 0010 | 0000 = 0011 =3(int)
每一种组合都对应唯一的一个二进制结果,这样将结果保存后,就一个用一个字段存储所有组合了。
然后我们有了这个计算结果后,还可以进行**反推状态,这里用&操作**
例如,刚才我们的一个计算结果是 3,0b0011
我们首先用 0011 与“重复”对应的 0001 进行&操作
0011 & 0001 = 0001 ≠ 0,表示这个计算结果是包含“重复”状态的。
我们在用 0011 与“过期”对应的 0010 进行&操作
0011 & 0010 = 0010 ≠ 0,表示这个计算结果是包含“过期”状态的。
我们在用 0011 与“优质”对应的 0100 进行&操作
0011 & 0100 = 0000 = 0,表示这个计算结果是不包含“优质”状态的。
通过与相应二进制码进行&操作后结果是否等于 0,可以判断状态。
除了进行反推,还需要个清除状态,可以去掉某个状态值。这一用到^操作
例如已知 0011 是包含重复状态,去除重复的话
0011 ^ 0001 = 0010 ,0010 表示只过期状态。
一句话总结下:
添加状态用|,去掉状态用^(这里有个小坑,之后说),判断状态用&
具体代码
1 |
|
生成状态值时,代码如下:
1 |
|
这里生成状态值时,只能给出所有组合,进行保存,如果想根据当前状态与给定状态,进行组合更新,需要像下面这样调用:
例如目前,已经是 0011 了,我们给定 expire=false,需要结果更新为 0001
- 0011 & 0010 = 0010 ≠0,表示目前是过期,去掉过期通过^处理,0011^0010 = 0001
- 0001 & 0010 = 0000 =0,表示原来就不是过期,所以不需要进行操作
这种单独修改,需要判断状态的转化,所以一般分为两步:
1、获取当前状态与目标状态
2、是-》是,否-》否,不需要操作;是到否,异或方式去掉状态;否到是,用或操作,将状态加上。
1 |
|
后续
这样就可以用一个字段进行存储了。其实我目前的方法不是最优解,实际上安卓系统在处理这种多状态时,已经在用这种二进制方式了,后续在研读安卓后,会尝试写一篇后续文章,优化目前的代码