核心: long(long数组) 和 位运算,
其存储结构elements并未直接存枚举本身,而是位标识,枚举存于elementType中的enumConstants中.
1、内部元素为枚举;
2、内部表示为位向量,使用“位标志”的替换形式。(类似0x1000000,按bit存储,用位运算进行相关操作);
3、全部是静态方法static;
4、根据传入的枚举类型判断组成长度,小于等于64则返回RegularEnumSet,否则JumboEnumSet。
/** * Creates an empty enum set with the specified element type. */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);}
------------------------------------------------------------------------------
RegularEnumSet
5、add源码
/** * Adds the specified element to this set if it is not already present. * @throws NullPointerException if <tt>e</tt> is null */public boolean add(E e) { typeCheck(e); // long oldElements = elements; elements |= (1L << ((Enum<?>)e).ordinal()); return elements != oldElements;}
每个枚举值都对应着自己的ordinal,第一个枚举值的ordinal为0,第二个为1,以此类推。
看看RegularEnumSet中elements的定义吧。关键点:位向量、2^k bit则说明存在该元素(每位都为1)。
/** * Bit vector representation of this set. The 2^k bit indicates the * presence of universe[k] in this set. */private long elements = 0L;
1、取枚举值的ordinal,初始为0;
2、1 << ordinal ;
3、与elements做 |= 运算。
例:
a.添加ordinal为0的枚举值,则计算后elements为1
b.添加ordinal为1的枚举值,则计算后elements为( 01|10 ) = 11(B)
c.添加ordinal为2的枚举值,则计算后elements为(011 | 100) = 111
So,EnumSet就是用long型来存储枚举,支持64位(long64位)。
而ordinal是个什么鬼呢?
是Enum抽象类的一个属性,源码如下:
private final int ordinal;/** * Returns the ordinal of this enumeration constant (its position * in its enum declaration, where the initial constant is assigned * an ordinal of zero). */public final int ordinal() { return ordinal; }
注意看:这里被调用的是ordinal()方法,以便于后续版本更新时,保持良好的兼容性(即使计算方式改变了,也不会影响原来的工程)。
6、contains 源码
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;}
1、1L << ((Enum)e).ordinal() ;
2、与elements做&运算。
例:
ordinal为2,则elements为111(B);
1L << ((Enum<?>)e).ordinal() 值为 100(B);
elements & (1L << ((Enum<?>)e).ordinal()) = 111(B)
111不等于0,说明包含该元素。
利用 long 和 位运算 实现EnumSet的快速存储和判断。
7、size源码
public int size() { return Long.bitCount(elements);}
bitCount返回的是参数二进制码中1的个数。如110(B)返回2。
源码如下,太过高深,在此就不剖析了。
public static int bitCount(long i)
{
i -= i >>> 1 & 6148914691236517205L;
i = (i & 3689348814741910323L) + (i >>> 2 & 3689348814741910323L);
i = i + (i >>> 4) & 1085102592571150095L;
i += i >>> 8;
i += i >>> 16;
i += i >>> 32;
return (int)i & 127;
}
JumboEnumSet
/** * Bit vector representation of this set. The ith bit of the jth * element of this array represents the presence of universe[64*j +i] * in this set. */private long elements[];
由RegularEnumSet中的long型转为long型数组。
public boolean add(E e) { typeCheck(e); int eOrdinal = e.ordinal(); int eWordNum = eOrdinal >>> 6; // 无符号右移,高位补0,去掉低位 // eWordNum 表示 eOrdinal 的高位
// 左移乘2,右移除2,移位位数对32取模( i>>33 等价于 i>>1) long oldElements = elements[eWordNum]; elements[eWordNum] |= (1L << eOrdinal); // 核心,Enum前64个元素全部放在elements[0]中
// elements[0]的值的二进制 第i位为1,则表示Enum的第i个元素存在于该集合,
// 引申[64*j +i],elements[j]的第i个元素为1。 boolean result = (elements[eWordNum] != oldElements); if (result) size++; return result;}
public boolean contains(Object e) { if (e == null) return false; Class<?> eClass = e.getClass(); if (eClass != elementType && eClass.getSuperclass() != elementType) return false; int eOrdinal = ((Enum<?>)e).ordinal(); return (elements[eOrdinal >>> 6] & (1L << eOrdinal)) != 0;}
NOte:正数一直无符号右移,将变为0。
例:
"eOrdinal" 65
"1L << eOrdinal" 2
"eOrdinal >>> 6" 1 // 待查元素的高位
"elements[eOrdinal >>> 6]" 35 // elements第j号位所有元素
"elements[eOrdinal >>> 6] & (1L << eOrdinal)" 2
// Redundant - maintained for performance
private int size = 0;
public int size() { return size;}