我们经常会定义一些常量值,来表示一些状态,每个常量值代表的意思都不一样,而且通过位运算 或 OR | ,可以将不同的样式合并到一个值中表示,这叫做位域(bit field)
public class Text {
private static final int STYLE_BOLD = 1<<0; //1 样式--粗体
private static final int STYLE_ITALIC = 1<<1; //2 样式--斜体
private static final int STYLE_UNDERLINE = 1<<2; //4 样式--下划线
private static final int STYLE_STRIKETHROUGH = 1<<3; //8 样式--删除线
public static final byte[] arr = {STYLE_BOLD, STYLE_ITALIC, STYLE_UNDERLINE, STYLE_STRIKETHROUGH};
//将样式分解之后得出样式的组合情况
public static void applyStyles(int style){
for (int i = 0, count = arr.length; i < count; i++){
int value = arr[i] & style;
if(value != 0){
System.out.println(value);
}
}
}
public static void main(String[] args) {
//我们传入5,代表粗体+下划线样式
Text.applyStyles(5);
}
}
猛一看,可能有点懵。为什么要这么写,我举个最简单的例子。
大家都做过多选题,比如有abcd四个选项,在上述例子中,
STYLE_BOLD 代表 a
STYLE_ITALIC 代表 b
STYLE_UNDERLINE 代表 c
STYLE_STRIKETHROUGH 代表 d
假如有个题的答案是 ab,那么我们就需要 int value = Text.STYLE_BOLD | Text.STYLE_ITALIC; 来表示;如果是acd, 那么就是 int value = Text.STYLE_BOLD | Text.STYLE_UNDERLINE | Text.STYLE_STRIKETHROUGH; 可能有疑问,问什么可以用这样方法标识值呢,为什么非得是 1、2、4、8 呢,我直接用0、1、2、3 来标识可以吗?
我们知道,位运算是把int值转换为二进制,然后计算的, int value = Text.STYLE_BOLD | Text.STYLE_ITALIC 值为 1+2=3, int value = Text.STYLE_BOLD |Text.STYLE_UNDERLINE | Text.STYLE_STRIKETHROUGH; 值为1+4+8=13;通过二进制可以发现,他们的值的组成都是单一的, 例如 3 只能 是 1 和 2 组成, 13只能是1和4和8组成,只要能看懂二进制都能明白这个道理,不懂的需要先熟悉一下二进制。二进制本身就是以2为基础,每次都乘以2获取值位移,所以这种情况用位移能标识值的唯一的分解法。 如果 我们选取了 0、1、2、3 来标识abcd这四个值,那么不定项选择中,如果答案为bc,那么值是 1+2=3,但问题来了,d选项的值也是3,那么3对应的就是两种可能,bc或者d,因为是不定项选择,我们也没法确定是哪个,所以不能随意标识值,只能选择那些组合出来的值只能被分解成唯一的几个子值,位移就是其中一个完美的选择。饶了这么多,只想解释一下上面那个方法的使用场景,有使用场景利于加深理解和记忆。
上述的方法中,我们会调用applyStyles(int styles)方法,
比如 new Text().applyStyles(Text.STYLE_BOLD | Text.STYLE_ITALIC); 我们在applyStyles(int styles) 方法中,通过 & 运算,分解出每个值,然后做相应的操作
位域的优缺点都非常明显,优点是利用了位运算,效率很高,节省内存。缺点是对程序员调试非常不友好,其输出只有数字,甚至比简单的 int 枚举形式更困难,需要人肉执行运算
幸运的是,java 中有 enumSet ,不仅保留了位域的位运算优点,且代码格式对程序员也非常友好。以上的代码可以改为
public class EnumSetTest {
public enum Style {
BOLD, ITALIC, UNDERLINE, STRIKETHROUGH;
}
public static void applyStyles(Set<Style> styles) {
for(Style style : styles){
// doSome(style);
};
}
public static void main(String[] args) {
EnumSet<Style> styles = EnumSet.of(Style.BOLD, Style.UNDERLINE);
applyStyles(styles);
}
}
总而言之,不能仅仅因为一个可枚举的类型要放到集合中,就使用位域来表示它。
所有文章无条件开放,顺手点个赞不为过吧!