问题
在 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;
}