EnumSet 的内部基本使用如下:

1
EnumSet<Frult> set = EnumSet.of(Frult.A, Frult.C, Frult.E);

为什么使用 EnumSet 可以参考 effective-java。

简单看 EnumSet 类声明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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 的迭代器实现方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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—