一、驚現問題
? 有一天,突然來了一個需求,問小明提交了哪些課程的作業?
? 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的狀態的序列
-
運用場景:
- 訂單實體,我們可能涉及到的狀態有,是否發貨,是否稽核,是否付款,是否接受等等.
- 棋牌遊戲使用者狀態
- 棋盤的點陣圖(棋盤模式)位置(域):上下左右 左上角 右上角 左下角 右下角 方位等待
- 使用者是否提交多項認證資料等
-
優勢:
- 佔用記憶體少
- 具有高效的拷貝、設值、比較運算等
- 位並行運算的可能
-
不足:
- 難於除錯
- 訪問單獨位的複雜性,易出錯
- 難於擴充套件
- 不符合物件導向的風格
- 不能處理所有的任務;需要大量的工作
- 喜歡嗎?喜歡就別愣著啊 點贊啊~~ 感謝大家關注我...據說點贊延年益壽哦。