1.EnumSet用法场景
1.存储一种类型枚举集合。
2.不允许存储重复枚举值。
3.线程不安全。
4.内部是二进制运算,效率极高。
public class Test {
enum K {
A,
B,
C
}
public static void main(String[] args) {
EnumSet<K> kk = EnumSet.noneOf(K.class);
kk.add(K.A);
kk.add(K.A);
kk.add(K.B);
System.err.println(kk); //[A, B]
kk.remove(K.A);
}
}
2.源码解析
1.静态工厂构造器设计模式
当枚举里面的元素个数(上面例子K枚举,元素个数是3) <= 64时,构造RegularEnumSet,否则构造JumboEnumSet,两者都是EnumSet的子类。
2.分析RegularEnumSet的add方法。
public boolean add(E e) {
typeCheck(e);
long oldElements = elements;
elements |= (1L << ((Enum<?>)e).ordinal());
return elements != oldElements;
}
// 只能存储一种类型的枚举原因
final void typeCheck(E e) {
Class<?> eClass = e.getClass();
if (eClass != elementType && eClass.getSuperclass() != elementType)
throw new ClassCastException(eClass + " != " + elementType);
}
每个枚举值都对应着自己的ordinal,第一个枚举值的ordinal为0,第二个为1(例如:K枚举, K.A的ordinal是0 K.B的ordinal是1 …)
假如 add(K.B) ,会经过以下运算:
- 1L << ((Enum<?>)e).ordinal() 等价于 …001 << 1 等于 …010,也就是把第二个位置置为1.
- 原来的elements值与010取或。(继承原来的1,并把新的…010第二个位置变成也是1)
总结就是,把K枚举看成一个数组 初始值是[0,0,0] , add K.B后,变成了[0,1,0], add K.C就变成了[1,1,0]
3.使用场景
实现像 BLOD|ITAI 的 同样功能。
public class Font {
enum Style {
/**默认格式*/
DEFAULT,
/**斜体*/
ITAI,
/**加粗*/
BLOD,
}
/* 存储当前字体的 所有style */
final EnumSet<Style> styles = EnumSet.noneOf(Style.class);
public Font addStyle(Style style) {
styles.add(style);
return this;
}
public static void main(String[] args) {
Font font = new Font();
// 是不是 跟 BLOD|ITAI 写法有点像?
font.addStyle(Style.BLOD).addStyle(Style.ITAI);
}
}