Jdk1.6 Collections Framework源码解析(11)-EnumSet
作者:大飞
- EnumSet是一种针对Enum类型提供的特殊的Set,每个EnumSet只能基于一个Enum类型来建立。
- EnumSet内部采用位域的方式建立(相当于bit数组),所以操作起来非常高效(几乎所有的基本操作都能在常数时间内完成),包括retainAll和retainAll这种批量操作也一样。
- EnumSet的迭代器中元素顺序按照Enum的自然序(即定义顺序)排列;迭代器是弱一致的,不会抛出ConcurrentModificationException。
- EnumSet的元素不允许为null;EnumSet非线程安全。
- 先看下EnumSet内部结构:
public abstract class EnumSet> extends AbstractSet implements Cloneable, java.io.Serializable { /** * The class of all the elements of this set. */ final Class elementType; /** * All of the values comprising T. (Cached for performance.) */ final Enum[] universe; EnumSet(Class elementType, Enum[] universe) { this.elementType = elementType; this.universe = universe; }
同时会发现,EnumSet的构造方法只是包内可见,外部没法直接构造。实际用起来也是通过of、allOf这样的方法来构造EnumSet实例的。下面先看一个比较重要的方法-noneOf:
public static> EnumSet noneOf(Class 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); } private static > E[] getUniverse(Class elementType) { return SharedSecrets.getJavaLangAccess() .getEnumConstantsShared(elementType); }
- 带着上面的2个疑问,我们来进行关键源码的分析:
先来解答第一个问题,接着上面getUniverse中的实现,我们先看下SharedSecrets的源码(可通过openjdk查看):
public class SharedSecrets { ... public static void setJavaLangAccess(JavaLangAccess jla) { javaLangAccess = jla; } public static JavaLangAccess getJavaLangAccess() { return javaLangAccess; }
原来在JVM初始化过程中会有初始化系统类的过程,这个过程的详细代码可参见hotspot/src/share/vm/runtime/thread.cpp中的call_initializeSystemClass方法,代码就不贴了。这个方法其实是调用了System类的initializeSystemClass方法:
/** * Initialize the system class. Called after thread initialization. */ private static void initializeSystemClass() { ... // Workaround until DownloadManager initialization is revisited. // Make JavaLangAccess available early enough for internal // Shutdown hooks to be registered setJavaLangAccess(); ... }
再看下这个setJavaLangAccess方法:
private static void setJavaLangAccess() { // Allow privileged classes outside of java.lang sun.misc.SharedSecrets.setJavaLangAccess(new sun.misc.JavaLangAccess(){ public sun.reflect.ConstantPool getConstantPool(Class klass) { return klass.getConstantPool(); } public void setAnnotationType(Class klass, AnnotationType type) { klass.setAnnotationType(type); } public AnnotationType getAnnotationType(Class klass) { return klass.getAnnotationType(); } public> E[] getEnumConstantsShared(Class klass) { return klass.getEnumConstantsShared(); } public void blockedOn(Thread t, Interruptible b) { t.blockedOn(b); } public void registerShutdownHook(int slot, Runnable r) { Shutdown.add(slot, r); } public int getStackTraceDepth(Throwable t) { return t.getStackTraceDepth(); } public StackTraceElement getStackTraceElement(Throwable t, int i) { return t.getStackTraceElement(i); } }); }
可见,其中的实现是直接调用了传入Class的getEnumConstantsShared方法,继续看:
T[] getEnumConstantsShared() { if (enumConstants == null) { if (!isEnum()) return null; try { final Method values = getMethod("values"); java.security.AccessController.doPrivileged (new java.security.PrivilegedAction() { public Object run() { values.setAccessible(true); return null; } }); enumConstants = (T[])values.invoke(null); } // These can happen when users concoct enum-like classes // that don't comply with the enum spec. catch (InvocationTargetException ex) { return null; } catch (NoSuchMethodException ex) { return null; } catch (IllegalAccessException ex) { return null; } } return enumConstants; }
好吧,我们来写一个简单的枚举:
public enum Laugh { HAHA, HEHE, HEIHEI }
然后用javap分析下这个class(javap -p -c 目标class路径):
public final class com.brokendreams.Laugh extends java.lang.Enum{ public static final com.brokendreams.Laugh HAHA; public static final com.brokendreams.Laugh HEHE; public static final com.brokendreams.Laugh HEIHEI; private static final com.brokendreams.Laugh[] ENUM$VALUES; static {}; Code: 0: new #1 // class com/brokendreams/Laugh 3: dup 4: ldc #14 // String HAHA 6: iconst_0 7: invokespecial #15 // Method " ":(Ljava/lang/String;I)V 10: putstatic #19 // Field HAHA:Lcom/brokendreams/Laugh; 13: new #1 // class com/brokendreams/Laugh 16: dup 17: ldc #21 // String HEHE 19: iconst_1 20: invokespecial #15 // Method " ":(Ljava/lang/String;I)V 23: putstatic #22 // Field HEHE:Lcom/brokendreams/Laugh; 26: new #1 // class com/brokendreams/Laugh 29: dup 30: ldc #24 // String HEIHEI 32: iconst_2 33: invokespecial #15 // Method " ":(Ljava/lang/String;I)V 36: putstatic #25 // Field HEIHEI:Lcom/brokendreams/Laugh; 39: iconst_3 40: anewarray #1 // class com/brokendreams/Laugh 43: dup 44: iconst_0 45: getstatic #19 // Field HAHA:Lcom/brokendreams/Laugh; 48: aastore 49: dup 50: iconst_1 51: getstatic #22 // Field HEHE:Lcom/brokendreams/Laugh; 54: aastore 55: dup 56: iconst_2 57: getstatic #25 // Field HEIHEI:Lcom/brokendreams/Laugh; 60: aastore 61: putstatic #27 // Field ENUM$VALUES:[Lcom/brokendreams/Laugh; 64: return private com.brokendreams.Laugh(java.lang.String, int); Code: 0: aload_0 1: aload_1 2: iload_2 3: invokespecial #31 // Method java/lang/Enum." ":(Ljava/lang/String;I)V 6: return public static com.brokendreams.Laugh[] values(); Code: 0: getstatic #27 // Field ENUM$VALUES:[Lcom/brokendreams/Laugh; 3: dup 4: astore_0 5: iconst_0 6: aload_0 7: arraylength 8: dup 9: istore_1 10: anewarray #1 // class com/brokendreams/Laugh 13: dup 14: astore_2 15: iconst_0 16: iload_1 17: invokestatic #35 // Method java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V 20: aload_2 21: areturn public static com.brokendreams.Laugh valueOf(java.lang.String); Code: 0: ldc #1 // class com/brokendreams/Laugh 2: aload_0 3: invokestatic #43 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum; 6: checkcast #1 // class com/brokendreams/Laugh 9: areturn }
首先看下RegularEnumSet:
class RegularEnumSet> extends EnumSet { /** * Bit vector representation of this set. The 2^k bit indicates the * presence of universe[k] in this set. */ private long elements = 0L; RegularEnumSet(Class elementType, Enum[] universe) { super(elementType, universe); }
下面利用我们上面定义的枚举,写个例子:
public static void main(String[] args) { try { EnumSetlaughs = EnumSet.noneOf(Laugh.class); /* * 0...0 0 0 0 0 0 0 0 0 * 56个 */ Field elementsField = laughs.getClass().getDeclaredField("elements"); elementsField.setAccessible(true); laughs.add(Laugh.HEHE); /* * 0...0 0 0 0 0 0 0 1 0 * 56个 HEHE */ long elements = elementsField.getLong(laughs); System.out.println(Long.toBinaryString(elements)); laughs.add(Laugh.HAHA); /* * 0...0 0 0 0 0 0 0 1 1 * 56个 HEHE HAHA */ elements = elementsField.getLong(laughs); System.out.println(Long.toBinaryString(elements)); laughs.add(Laugh.HEIHEI); /* * 0...0 0 0 0 0 0 1 1 1 * 56个 HEIHEI HEHE HAHA */ elements = elementsField.getLong(laughs); System.out.println(Long.toBinaryString(elements)); } catch (Exception e) { e.printStackTrace(); } }
输出如下:
10 11 111
顺便看下add方法的实现:
public boolean add(E e) { typeCheck(e); long oldElements = elements; elements |= (1L << ((Enum)e).ordinal()); return elements != oldElements; }
再看一个EnumSet的方法-allOf的实现:
public static> EnumSet allOf(Class elementType) { EnumSet result = noneOf(elementType); result.addAll(); return result; }
首先调用了noneOf,得到一个空的EnumSet集合,然后调用addAll方法添加所有元素,看看RegularEnumSet中addAll的实现细节:
void addAll() { if (universe.length != 0) elements = -1L >>> -universe.length; }
再看下JumboEnumSet:
class JumboEnumSet> extends EnumSet { /** * 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[]; // Redundant - maintained for performance private int size = 0; JumboEnumSet(Class elementType, Enum[] universe) { super(elementType, universe); elements = new long[(universe.length + 63) >>> 6]; }
剩下的代码不看了,都是位操作,就不在这里啰嗦了,EnumSet的源码解析就到这里!有不对的地方请指正!