開發技巧:秀兒,如何才能一個欄位表示八個狀態啊

錦成同學發表於2019-09-21

一、驚現問題

? 有一天,突然來了一個需求,問小明提交了哪些課程的作業?

開發技巧:秀兒,如何才能一個欄位表示八個狀態啊
? A:語文 B:數學 C:英語 D:物理 E:化學 F:生物 G:歷史 H:地理 .....

  • 這麼多課程.....我要8個欄位? 萬一還有課程呢?我還要再加欄位?

  • 我肯定不慌啊,我一個欄位搞定。

二、解決之道

通過一個 int或者long 欄位,來新增多個 標誌或者狀態. 一個int或者long欄位,能夠管理多個標記(狀態)值. 如此神奇的操作怎樣實現的呢? 答案就是通過位運算來實現.

  • 像這種,獨立狀態(標記)之間相互組合可以產生新的狀態(標記),且每個獨立狀態(標記)只有true或者false值的,我們可以使用位狀態的概念來管理這些狀態.

  • 它的核心思想就是將, int 數值看做是 二進位制數位表示.如果有四個狀態就可以像這樣 0000,用四位二進位制表示,每一個二進位制位都可以表示一種狀態. 然後通過 位運算,來提取或新增標記位.四位對應的組合狀態有16個. 而我們,只需要通過一個int變數就能夠管理這些狀態.

  • 當參與的狀態(標記)越多時,如果使用單獨的標記變數,就需要生成越多的變數,而用位域,這種獨立狀態為不管有多少個,都可以用一個變數表示.int型別最多存放32個獨立狀態.

  • 位操作基礎:

java中提供的基礎位運算子有 與(&),或(|),非(~),異或(^),左移<<,右移(>>)無符號右移(>>>).

除了位非(~)是一元操作符外,其它的都是二元操作符。

  • 下面只介紹本文中,使用到的位操作:

1.位與

A & B : A和B對應的二進位制數位都為1時,結果才為1,其他情況為0.

A     =  001101 // 13
B     =  100101 // 37
A & B =  000101 // 5
複製程式碼

2.位或

A | B : A和B對應的二進位制數位都為0時,結果才為0,其他情況為1.

A     =  001101 // 13
B     =  100101 // 37
A | B =  101101 // 45
複製程式碼

3.位非

~A : 將a的二進位制表示每一位進行取反操作,0變1,1變0.相當於相反數 - 1

A     =  001101 // 13
~A    =  11111111111111111111111111110010 // int32位,補碼錶示,第一位為符號位
// 根據上訴補碼轉原碼為
//       10000000000000000000000000001110 // -14
複製程式碼

4.左移操作

A << B:將A的二進位制表示的每一位向左移B位, 左邊超出的位截掉,右邊不足的位補0。在取值範圍內,移動一位相當於乘2.

A     =  001101 // 13
A << 1 = 011010 // 26
複製程式碼

三、實戰操作

/**
 * @author LiJing
 * @ClassName: BitStatusUtils
 * @Description: 位運算處理狀態的工具類
 * @date 2019/9/21
 * @time 10:38
 */
public class BitStatusUtils {

    //A:語文 B:數學 C:英語 D:物理 E:化學 F:生物 G:歷史 H:地理 

    // 二進位制表示 0001 沒有交任何作業
    public static final int NONE              = 1<<0;   //預設
    public static final int CHINESE           = NONE<<1;//語文
    public static final int MATH              = NONE<<2;//數學
    public static final int ENGLISH           = NONE<<3;//英語
    public static final int PHYSICS           = NONE<<4;//物理
    public static final int CHEMISTRY         = NONE<<5;//化學
    public static final int BIOLOGY           = NONE<<6;//生物
    public static final int HISTORY           = NONE<<7;//歷史
    public static final int GEOGRAPHY         = NONE<<8;//地理

    public static final int ALL =NONE|CHINESE|MATH|ENGLISH|PHYSICS|CHEMISTRY|BIOLOGY|HISTORY|GEOGRAPHY;

    /**
     * @param status 所有狀態值
     * @param value  需要判斷狀態值
     * @return 是否存在
     */
    public static boolean hasStatus(long status, long value) {
        return (status & value) != 0;
    }

    /**
     * @param status 已有狀態值
     * @param value  需要新增狀態值
     * @return 新的狀態值
     */
    public static long addStatus(long status, long value) {
        if (hasStatus(status, value)) {
            return status;
        }
        return (status | value);
    }

    /**
     * @param status 已有狀態值
     * @param value  需要刪除狀態值
     * @return 新的狀態值
     */
    public static long removeStatus(long status, long value) {
        if (!hasStatus(status, value)) {
            return status;
        }
        return status ^ value;
    }
    
     /**是否交了含有全部狀態
     * @param status 
     * @return
     */
    public static boolean hasAllStatus(long status) {
        return (status & ALL) == ALL;
    }

    public static void main(String[] args) {

        long status = addStatus(NONE, CHINESE);
        System.out.println("小明交了語文作業:" + status);

        status = addStatus(status, PHYSICS);
        System.out.println("小明又交了物理作業:" + status);

        status = addStatus(status, HISTORY);
        System.out.println("小明還交了歷史作業:" + status);

        status = removeStatus(status, HISTORY);
        System.out.println("小明撤銷了歷史作業:" + status);

        System.out.println("小明是否交了語文作業:" + hasStatus(status, CHINESE));
        System.out.println("小明是否交了歷史作業:" + hasStatus(status, HISTORY));
        System.out.println("小明是否交了生物作業:" + hasStatus(status, BIOLOGY));
        System.out.println("小明是否交了全部作業:" + hasAllStatus(status));
    }
}
複製程式碼

測試結果:

小明交了語文作業:3
小明又交了物理作業:19
小明還交了歷史作業:147
小明撤銷了歷史作業:19
小明是否交了語文作業:true
小明是否交了歷史作業:false
小明是否交了生物作業:false
小明是否交了全部作業:false
複製程式碼

四、完美小結

  • 記憶體由位元組組成.每個位元組由8位bit組成,每個bit狀態只能是0或1.

  • 所謂位模式,無非就是變數所佔用記憶體的所有bit的狀態的序列

  • 運用場景:

    1. 訂單實體,我們可能涉及到的狀態有,是否發貨,是否稽核,是否付款,是否接受等等.
    2. 棋牌遊戲使用者狀態
    3. 棋盤的點陣圖(棋盤模式)位置(域):上下左右 左上角 右上角 左下角 右下角 方位等待
    4. 使用者是否提交多項認證資料等
  • 優勢:

    1. 佔用記憶體少
    2. 具有高效的拷貝、設值、比較運算等
    3. 位並行運算的可能
  • 不足:

    1. 難於除錯
    2. 訪問單獨位的複雜性,易出錯
    3. 難於擴充套件
    4. 不符合物件導向的風格
    5. 不能處理所有的任務;需要大量的工作
  • 喜歡嗎?喜歡就別愣著啊 點贊啊~~ 感謝大家關注我...據說點贊延年益壽哦。

相關文章