問題
在 Java 中,如何使用一個 byte 類型的變量保存多個 boolean 類型的值?
場景
請選擇喜愛的球類運動(多選):
□ 足球 | □ 籃球 | □ 乒乓球 |
---|---|---|
□ 臺球 | □ 排球 | □ 羽毛球 |
在如上所描述的場景中,如何使用一個 byte 類型的變量來保存各項是否選中?
回答
常規思路是定義 6 個 boolean 類型的變量,每個變量保存一項的狀態, 選中為 true
, 否則為 false
。
boolean footballIsChecked = false;
...
boolean badmintonIsChecked = false;
在 Java 中,byte 類型的數據使用 8 位進行保存,每一位的值可能是 0
或 1
。理論上來講,byte 類型存儲空間的每一位可以表示一個 boolean 類型的值,1
表示 true
,0
表示 false
。
byte sportCheckedState;
sportCheckedState = 0b0000_0000; //都沒選中
sportCheckedState = 0b0000_0001; //選中了足球
sportCheckedState = 0b0000_0010; //選中了籃球
因此,我們的需要解決問題變成了如何將 byte 類型某一位存儲空間的值設置為 0
或 1
, 如何讀取某一位的值。
設置 byte 類型某一位的存儲空間的值
如何將某一位的值設置為 0
呢?
0 & 0 = 0
0 & 1 = 0
答:該位和 0
進行 &
運算。
如何將某一位的值設置為 1
呢?
1 | 0 = 1
1 | 1 = 1
答:該位和 1
進行 |
運算。
既然是設置某一位的值,就應該不影響其他位的值。
執行 &
運算時,如何讓運算結果和運算數相等?
1 & 0 = 0
1 & 1 = 1
答:當另外一個運算數為 1
時,運算結果和此運算數相同。
執行 |
運算時,如何讓運算結果和運算數相等?
0 | 0 = 0
0 | 1 = 1
答:當另外一個運算數為 0
時,運算結果和此運算數相同。
綜上,設置某一位為 0
時,進行 &
運算,輔助運算數(另外一個參與運算的數)應該符合這樣的規則:目標位為0
, 其他位為 1
。 例如設置最后一位,輔助運算數為 1111_1110
;
1 0 1 0
& 1 1 1 0 //輔助運算數
----------
1 0 1 0 //運算結果的前 3 位不變,最后一位設置為 0
1 0 1 1
& 1 1 1 0 //輔助運算數
----------
1 0 1 0 //運算結果的前 3 位不變,最后一位設置為 0
設置某一位為1
時,進行 |
運算,輔助運算數應該符合這樣的規則:目標位為 1
, 其它位為 0
。例如設置最后一位,輔助運算數為 0000_0001
。
1 0 1 0
| 0 0 0 1 //輔助運算數
----------
1 0 1 1 //運算結果的前 3 位不變,最后一位設置為 1
1 0 1 1
| 0 0 0 1 //輔助運算數
----------
1 0 1 1 //運算結果的前 3 位不變,最后一位設置為 1
讀取 byte 類型某一位的存儲空間的值
和 1
進行 &
時,運算結果和另外一個運算數相同。
1 & 0 = 0
1 & 1 = 1
和 0
進行 &
時,運算結果為0
。
0 & 0 = 0
0 & 1 = 0
為了方便,我們希望 &
運算結果的其他位為 0
,根據 0000_0000
的運算結果可以推算出目標位的值為 0
;根據目標位為 1
,其它位的值為 0
的運算結果可以推算出目標位的值為 1
。
所以,構造出的輔助運算數應該符合這樣的規則:目標位的值為 1
,其他位的值為 0
。例如,倒數第二位應該選取的輔助運算數為 0000_0010
。
1 0 1 0
& 0 0 1 0 //輔助運算數
----------
0 0 1 0 //運算結果的 1、2、4 位置為 0,第 3 為值為 1 ,即運算數第 3 位值為 1
1 0 0 0
& 0 0 1 0 //輔助運算數
----------
0 0 0 0 //運算結果的 1、2、4 位置為 0,第 3 為值為 0 ,即運算數第 3 位值為 0
據此,我們可以得出結論:運算結果每一位的值都是 0
時,目標位的值為 0
;運算結果存在值不為 0
的位時,目標位的值為 1
。
示例
// 每個狀態用一位表示,1 選中, 0 未選中
private static final byte CHECKED_FOOTBALL = 0b0000_0001;
private byte state = STATE_VALUE_DEFAULT;
public void setFootballCheckedState(boolean isChecked) {
if (isChecked) {
state = (byte) (state | CHECKED_FOOTBALL);
} else {
state = (byte) (state & ~CHECKED_FOOTBALL);
}
}
public boolean footballIsChecked() {
return (state & CHECKED_FOOTBALL) == CHECKED_FOOTBALL;
}