EnumSet
EnumSet是与枚举类型一起使用的专用Set集合,EnumSet的所有元素必须都是枚举类型。
内部实现是位向量,是一种高效的位运算操作。由于直接存储和操作都是bit,时间和空间性能都很可观。不允许使用null元素。线程不安全。
创建一个EnumSet不能使用new创建。因为是抽象类,使用提供的工厂方法创建。
创建一个具有指定元素类型的空EnumSet。
EnumSet<E> noneOf(Class<E> elementType)
//创建一个指定元素类型并包含所有枚举值的EnumSet
<E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType)
// 创建一个包括枚举值中指定范围元素的EnumSet
<E extends Enum<E>> EnumSet<E> range(E from, E to)
// 初始集合包括指定集合的补集
<E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s)
// 创建一个包括参数中所有元素的EnumSet
<E extends Enum<E>> EnumSet<E> of(E e)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)
<E extends Enum<E>> EnumSet<E> of(E first, E... rest)
//创建一个包含参数容器中的所有元素的EnumSet
<E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s)
<E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c)
EnumSet内部使用位向量实现的。是一个抽象类存在两个子类,RegularEnumSet和JumbpEnumSet。RegularEnumSet使用一个long类型的变量作为位向量,long类型的位长度是64,因此可以存储64个枚举实例的标志位。JumboEnumSet使用一个long类型的数组,当枚举个数超过64时,就会采用long数组的方式存储。
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, java.io.Serializable
{
//表示枚举类型
final Class<E> elementType;
//存储该类型信息所表示的所有可能的枚举实例
final Enum<?>[] universe;
//..........
}
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
//根据EnumMap中的一样,获取所有可能的枚举实例
Enum<?>[] universe = getUniverse(elementType);
if (universe == null)
throw new ClassCastException(elementType + " not an enum");
if (universe.length <= 64)
//枚举个数小于64,创建RegularEnumSet
return new RegularEnumSet<>(elementType, universe);
else
//否则创建JumboEnumSet
return new JumboEnumSet<>(elementType, universe);
}
实现类的构造函数
//RegularEnumSet构造
RegularEnumSet(Class<E>elementType, Enum<?>[] universe) {
super(elementType, universe);
}
//JumboEnumSet构造
JumboEnumSet(Class<E>elementType, Enum<?>[] universe) {
super(elementType, universe);
elements = new long[(universe.length + 63) >>> 6];
}
在RegularEnumSet类和JumboEnumSet类中都存在一个elements变量,用于记录位向量的操作。
//RegularEnumSet
class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
private static final long serialVersionUID = 3411599620347842686L;
//通过long类型的elements记录位向量的操作
private long elements = 0L;
//.......
}
//对于JumboEnumSet则是:
class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> {
private static final long serialVersionUID = 334349849919042784L;
//通过long数组类型的elements记录位向量
private long elements[];
//表示集合大小
private int size = 0;
//.............
}
add操作
public boolean add(E e) {
//检测是否为枚举类型
typeCheck(e);
//记录旧elements
long oldElements = elements;
//执行位向量操作,是不是很熟悉?
//数组版:a[i >> SHIFT ] |= (1 << (i & MASK))
elements |= (1L << ((Enum)e).ordinal());
return elements != oldElements;
}
remove操作
//RegularEnumSet类实现
public boolean remove(Object e) {
if (e == null)
return false;
Class eClass = e.getClass();
if (eClass != elementType && eClass.getSuperclass() != elementType)
return false;
long oldElements = elements;
//将int型变量j的第k个比特位设置为0,即j= j&~(1<<k)
//数组类型:a[i>>SHIFT] &= ~(1<<(i &MASK));
elements &= ~(1L << ((Enum)e).ordinal());//long遍历类型操作
return elements != oldElements;
}
//JumboEnumSet类的remove实现
public boolean remove(Object e) {
if (e == null)
return false;
Class<?> eClass = e.getClass();
if (eClass != elementType && eClass.getSuperclass() != elementType)
return false;
int eOrdinal = ((Enum<?>)e).ordinal();
int eWordNum = eOrdinal >>> 6;
long oldElements = elements[eWordNum];
//与a[i>>SHIFT] &= ~(1<<(i &MASK));相同
elements[eWordNum] &= ~(1L << eOrdinal);
boolean result = (elements[eWordNum] != oldElements);
if (result)
size--;
return result;
}
contain和containAll方法
public boolean contains(Object e) {
if (e == null)
return false;
Class eClass = e.getClass();
if (eClass != elementType && eClass.getSuperclass() != elementType)
return false;
//先左移再按&操作
return (elements & (1L << ((Enum)e).ordinal())) != 0;
}
public boolean containsAll(Collection<?> c) {
if (!(c instanceof RegularEnumSet))
return super.containsAll(c);
RegularEnumSet<?> es = (RegularEnumSet<?>)c;
if (es.elementType != elementType)
return es.isEmpty();
//~elements取反相当于elements补集,再与es.elements进行&操作,如果为0,
//就说明elements补集与es.elements没有交集,也就是es.elements是elements的子集
return (es.elements & ~elements) == 0;
}