在進行應用開發的時候,相信總是會有些數據的數據結構中含有一些二元屬性,如該條記錄是否對用戶可見、是否為待辦記事等等。而這些屬性也是需要存到數據庫中的。
那這樣的屬性,在內存中使用的數據結構中可能表現為一個布爾值,存到數據庫中還能是布爾類型嗎?
以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] Datatypes In SQLite Version 3
[2] Java基本數據類型總結