手机移动程序开发架构算法设计模式和编程理论Android开发

数据库存储二元值的优化方案,位存储

2016-12-31  本文已影响127人  Shawlaw

在进行应用开发的时候,相信总是会有些数据的数据结构中含有一些二元属性,如该条记录是否对用户可见、是否为待办记事等等。而这些属性也是需要存到数据库中的。

那这样的属性,在内存中使用的数据结构中可能表现为一个布尔值,存到数据库中还能是布尔类型吗?

以Android开发为例,Android中使用的数据库系统是SQLite,SQLite支持的存储格式根据官网的文档[1] 有如下这些:

官网文档截图

可以看到并没有布尔类型这种存储格式,所以开发人员常常都是用一个Integer的字段来存储一个布尔值属性。既然数据库都用了Integer,许多开发人员也就把数据结构中的boolean也改成了int。

但是在Java中,一个boolean只占一个二进制位,而一个int占了32个二进制位[2] 。整整差了31倍!那这样子的使用未免也太浪费了吧!

既然一个int都能存32位,那就直接用一个int值来存32个二元属性。一般的数据结构都不会用到那么多个二元属性,哪怕是进行了多次业务升级之后。而且我觉得吧,如果真的用到了那么多二元属性,建议重新考虑数据结构设计的合理性。

实现的方式很简明易懂,位运算。先上工具类代码。

/**
 * 用于帮助开发者在数据结构中使用Int值存储二元属性的工具类
 * Created by Shawlaw on 2016/12/4.
 */

public class BitHelper {
    /**
     * 给指定的int数值的特定位置0或置1
     * @param src 用于存储二元值的int数
     * @param bitMask 指定位的掩码,可通过{@link #getDeterminedBitMask(int)}取得指定位的对应掩码
     * @param bitValue 要给指定位设定的值
     * @return 置值完毕后的int数
     */
    public static int setDeterminedBit(int src, int bitMask, boolean bitValue){
        if (bitValue) {
            src |= bitMask;
        } else {
            src &= (~bitMask);
        }
        return src;
    }

    /**
     * 取得指定的int数值中的特定位上的值
     * @param src 用于存储二元值的int数
     * @param bitMask 指定位的掩码,可通过{@link #getDeterminedBitMask(int)}取得指定位的对应掩码
     * @return 指定位的值是否为1,为1则返回true,为0则返回false
     */
    public static boolean getDeterminedBit(int src, int bitMask){
        return (src & bitMask ) != 0;
    }

    /**
     * 获取int数的指定位的位运算掩码,一般配合{@link #getDeterminedBit(int, int)}或{@link #setDeterminedBit(int, int, boolean)}方法一起使用
     * @param reverseIndex 从最右即最低位为1数起的位数,最大为32,因为int数为4字节最大32位。
     * @return 位运算掩码
     */
    public static int getDeterminedBitMask(int reverseIndex){
        if (reverseIndex > 32 || reverseIndex < 1) {
            throw new RuntimeException("Index must between 1 to 32. The current index is "+reverseIndex);
        }
        return 1 << (reverseIndex - 1);
    }
}

工具类里面一共就三个方法,分别用于设置特定位的值、取特定位的值以及取特定位的运算掩码。
使用样例代码,如下:

public class MyModel{
    private boolean mIsNewUser;
    private boolean mIsRedDotShowed;

    private int mBitStatus;

    private final static int IS_NEW_USER_BIT_MASK = BitHelper.getDeterminedBitMask(1);
    private final static int IS_RED_DOT_SHOWED_BIT_MASK = BitHelper.getDeterminedBitMask(2);

    /**
     * 从数据库或网络请求取到数据**之后**,执行这个方法,从数值中解析各个位的状态到内存中的boolean值中。
     */
    public void restoreStatusFromInt(){
        mIsNewUser = BitHelper.getDeterminedBit(mBitStatus, IS_NEW_USER_BIT_MASK);
        mIsRedDotShowed = BitHelper.getDeterminedBit(mBitStatus, IS_RED_DOT_SHOWED_BIT_MASK);
    }

    /**
     * 要写入数据库或发网络请求**之前**,执行这个方法,把内存中的boolean值写入到数值中。
     */
    public void storeStatusToInt(){
        mBitStatus = BitHelper.setDeterminedBit(mBitStatus, IS_NEW_USER_BIT_MASK, mIsNewUser);
        mBitStatus = BitHelper.setDeterminedBit(mBitStatus, IS_RED_DOT_SHOWED_BIT_MASK, mIsRedDotShowed);
    }
   ....省略了通用的Setter和Getter方法....
}

讲完了源代码和样例代码,再来说说这样实现的优点和缺点。
优点:

  1. 节省空间耗费。
  2. 业务弹性强,尤其是当内存数据结构发生多次变化时,数据库表头不用同样地多次更改。

缺点: 对代码调用次序有硬性要求。

所以使用的时候还是得根据开发需求来确定要不要这样实现。


参考文献:
[1] Datatypes In SQLite Version 3
[2] Java基本数据类型总结

上一篇下一篇

猜你喜欢

热点阅读