EnumSet 的内部基本使用如下:
EnumSet<Frult> set = EnumSet.of(Frult.A, Frult.C, Frult.E);
为什么使用 EnumSet 可以参考 effective-java。
简单看 EnumSet 类声明:
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> implements Cloneable, java.io.Serializable { /** * The class of all the elements of this set. */ final Class<E> elementType; /** * All of the values comprising T. (Cached for performance.) */ final Enum<?>[] universe; private static Enum<?>[] ZERO_LENGTH_ENUM_ARRAY = new Enum<?>[0]; public static <E extends Enum<E>> EnumSet<E> of(E e) { EnumSet<E> result = noneOf(e.getDeclaringClass()); result.add(e); return result; } public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) { Enum<?>[] universe = getUniverse(elementType); if (universe == null) throw new ClassCastException(elementType + " not an enum"); if (universe.length <= 64) return new RegularEnumSet<>(elementType, universe); else return new JumboEnumSet<>(elementType, universe); } }
两个属性,elementType 存放枚举类型的 class,universe 是所有枚举的数组。
在 noneOf 方法当中,创建具体的 Enum 实现实例,如果 Enum 的长度大于了 64 创建 JumboEnumSet,小于等于 64 创建 RegularEnumSet。
只分析 RegularEnumSet 的情况:
class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> { private static final long serialVersionUID = 3411599620347842686L; private long elements = 0L; public int size() { return Long.bitCount(elements); } public boolean isEmpty() { return elements == 0; } public boolean add(E e) { typeCheck(e);//检查 Enum 的类型是否符合 long oldElements = elements; elements |= (1L << ((Enum<?>)e).ordinal()); return elements != oldElements; } }
RegularEnumSet 里面就是用了一个 long 型的 elements 来存放,因为 long 是 8 个字节,一共 64 bit。并且每个枚举的元素都有 ordinal 属性,只要添加进来就根据 ordinal 来或运算就可以了。
size 方法就是看指定 long 值的二进制补码表示形式中的 1 位的数量。
下面代码是 RegularEnumSet 的迭代器实现方式:
private class EnumSetIterator<E extends Enum<E>> implements Iterator<E> { long unseen; long lastReturned = 0; EnumSetIterator() { unseen = elements; } public boolean hasNext() { return unseen != 0; } @SuppressWarnings("unchecked") public E next() { if (unseen == 0) throw new NoSuchElementException(); lastReturned = unseen & -unseen; unseen -= lastReturned; return (E) universe[Long.numberOfTrailingZeros(lastReturned)]; } public void remove() { if (lastReturned == 0) throw new IllegalStateException(); elements &= ~lastReturned; lastReturned = 0; } }
迭代器 next 方法,就是从最低位取出元素,并且返回。
详细的方式就是,unseen 与运算 unseen 的相反数。结果就是每次都会把最后位是 1 的值取到,然后在通过 Long.numberOfTrailingZeros 转换成 2 的幂次数,从数组当中取到枚举元素。
最后就是那这个 lastReturned 在 unseen 当中减掉。
其他的不分析,主要是位运算,以及2进展补码的精妙之处,虽然还没搞清除为什么可以这样。。。。
以后在研究为何如此精妙。
—EOF—