EnumSet源码分析

来源:互联网 发布:太湖雪蚕丝被知乎 编辑:程序博客网 时间:2024/06/06 05:46
核心: 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;
}


1 0
原创粉丝点击