JAVA-枚举
sschrodinger
2018/11/5
枚举基本形式
public enum DAY {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY;
/**
** 以下可有可无,若无则SUNDAY结尾不加分号
**/
//初始化函数
public DAY() {}
//静态变脸
public static final int A = 10;
//非静态变量
public int b = 20;
//普通函数
public void print1() {}
//抽象函数
abstract void print2() {}
//静态函数
public static print3() {}
}
枚举的基本形式如上所示,基本可以看出,除了需要在类中申明需要枚举的常量之外,和普通的类没有任何区别。
实现原理
我们看最简单的例子。下例是一个只有两个枚举类型的枚举类,我们利用 javac
编译此文件,然后利用 javap
反汇编观看编译后的结果。
public enum DAY {
DAY,
NIGHT
}
# javap -c -p DAY.class
Classfile /D:/tmp/DAY.class
Last modified 2018-11-5; size 729 bytes
MD5 checksum e27398b494fef011953bd3e5dfc58513
Compiled from "DAY.java"
public final class DAY extends java.lang.Enum<DAY>
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
Constant pool:
#1 = Fieldref #4.#32 // DAY.$VALUES:[LDAY;
#2 = Methodref #33.#34 // "[LDAY;".clone:()Ljava/lang/Object;
#3 = Class #17 // "[LDAY;"
#4 = Class #13 // DAY
#5 = Methodref #12.#35 // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
#6 = Methodref #12.#36 // java/lang/Enum."<init>":(Ljava/lang/String;I)V
#7 = String #13 // DAY
#8 = Methodref #4.#36 // DAY."<init>":(Ljava/lang/String;I)V
#9 = Fieldref #4.#37 // DAY.DAY:LDAY;
#10 = String #15 // NIGHT
#11 = Fieldref #4.#38 // DAY.NIGHT:LDAY;
#12 = Class #39 // java/lang/Enum
#13 = Utf8 DAY
#14 = Utf8 LDAY;
#15 = Utf8 NIGHT
#16 = Utf8 $VALUES
#17 = Utf8 [LDAY;
#18 = Utf8 values
#19 = Utf8 ()[LDAY;
#20 = Utf8 Code
#21 = Utf8 LineNumberTable
#22 = Utf8 valueOf
#23 = Utf8 (Ljava/lang/String;)LDAY;
#24 = Utf8 <init>
#25 = Utf8 (Ljava/lang/String;I)V
#26 = Utf8 Signature
#27 = Utf8 ()V
#28 = Utf8 <clinit>
#29 = Utf8 Ljava/lang/Enum<LDAY;>;
#30 = Utf8 SourceFile
#31 = Utf8 DAY.java
#32 = NameAndType #16:#17 // $VALUES:[LDAY;
#33 = Class #17 // "[LDAY;"
#34 = NameAndType #40:#41 // clone:()Ljava/lang/Object;
#35 = NameAndType #22:#42 // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
#36 = NameAndType #24:#25 // "<init>":(Ljava/lang/String;I)V
#37 = NameAndType #13:#14 // DAY:LDAY;
#38 = NameAndType #15:#14 // NIGHT:LDAY;
#39 = Utf8 java/lang/Enum
#40 = Utf8 clone
#41 = Utf8 ()Ljava/lang/Object;
#42 = Utf8 (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
{
public static final DAY DAY;
descriptor: LDAY;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
public static final DAY NIGHT;
descriptor: LDAY;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
private static final DAY[] $VALUES;
descriptor: [LDAY;
flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
public static DAY[] values();
descriptor: ()[LDAY;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: getstatic #1 // Field $VALUES:[LDAY;
3: invokevirtual #2 // Method "[LDAY;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[LDAY;"
9: areturn
LineNumberTable:
line 1: 0
public static DAY valueOf(java.lang.String);
descriptor: (Ljava/lang/String;)LDAY;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: ldc #4 // class DAY
2: aload_0
3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #4 // class DAY
9: areturn
LineNumberTable:
line 1: 0
private DAY();
descriptor: (Ljava/lang/String;I)V
flags: ACC_PRIVATE
Code:
stack=3, locals=3, args_size=3
0: aload_0
1: aload_1
2: iload_2
3: invokespecial #6 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
6: return
LineNumberTable:
line 1: 0
Signature: #27 // ()V
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=4, locals=0, args_size=0
0: new #4 // class DAY
3: dup
4: ldc #7 // String DAY
6: iconst_0
7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9 // Field DAY:LDAY;
13: new #4 // class DAY
16: dup
17: ldc #10 // String NIGHT
19: iconst_1
20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
23: putstatic #11 // Field NIGHT:LDAY;
26: iconst_2
27: anewarray #4 // class DAY
30: dup
31: iconst_0
32: getstatic #9 // Field DAY:LDAY;
35: aastore
36: dup
37: iconst_1
38: getstatic #11 // Field NIGHT:LDAY;
41: aastore
42: putstatic #1 // Field $VALUES:[LDAY;
45: return
LineNumberTable:
line 2: 0
line 3: 13
line 1: 26
}
Signature: #29 // Ljava/lang/Enum<LDAY;>;
SourceFile: "DAY.java"
我们可以得到经过编译器编译的枚举类型结果为
public final class DAY extends java.lang.Enum<DAY> {
public static final DAY DAY;
public static final DAY NIGHT;
//编译器增加
private static final DAY[] $VALUES;
public static DAY[] values() {
return $VALUES.clone();
}
public static DAY valueOf(String) {
return (DAY) Enum.valueOf(DAY.class, name);
}
private DAY(String name, int seq) {
super.init(name, seq);
}
static {
DAY = new DAY("DAY", 0);
NIGHT = new DAY("NIGHT", 1);
$VALUES = new DAY[2];
$VALUES[0] = DAY;
$VALUES[1] = NIGHT;
}
}
可以看到,编译器自动将我们的枚举类型转化成了一个继承了 Enum 的 final 类。我们先来看看Enum这个类型的特征。java源码如下所示:
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
private final String name;
public final String name() {
return name;
}
private final int ordinal;
public final int ordinal() {
return ordinal;
}
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public String toString() {
return name;
}
public final boolean equals(Object other) {
return this==other;
}
public final int hashCode() {
return super.hashCode();
}
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
@SuppressWarnings("unchecked")
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}
protected final void finalize() { }
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}
private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
}
可以发现,Enum主要保存了两个 final 的私有域,用来保存枚举的名称和序号。
Note
- Enum 的toString方法返回枚举变量的名字。
- compareTo方法实际上是比较的两个枚举的序号关系。
- 重写了readObject和readObjectNoData两个方法,使得Enum不能够序列化。
回到编译好的枚举类型来,我们发现编译器帮助我们的事很像创建了一个多例模式的Enum子类。编译器将所有的枚举变量变成静态类型存储在类中。同时通过valueOf这个静态方法得到我们需要的枚举常量。
既然枚举类型在编译之后也是普通类,那么我们也就可以在枚举类型种创建各种方法了,例子如下:
public enum DAY {
DAY("hello"),
NIGHT("Good night");
private String arg;
private static final int a = 10;
DAY(String arg) {
this.arg = arg;
}
void print() {
System.out.println(toString() + arg);
}
}
同样,利用javap工具查看编译之后的class文件,结果如下:
Classfile /D:/tmp/DAY.class
Last modified 2018-11-6; size 1278 bytes
MD5 checksum b27b113e328d46d256f8bea28ca55ce1
Compiled from "DAY.java"
public final class DAY extends java.lang.Enum<DAY>
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
Constant pool:
#1 = Fieldref #4.#50 // DAY.$VALUES:[LDAY;
#2 = Methodref #51.#52 // "[LDAY;".clone:()Ljava/lang/Object;
#3 = Class #33 // "[LDAY;"
#4 = Class #23 // DAY
#5 = Methodref #22.#53 // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
#6 = Methodref #22.#54 // java/lang/Enum."<init>":(Ljava/lang/String;I)V
#7 = Fieldref #4.#55 // DAY.arg:Ljava/lang/String;
#8 = Fieldref #56.#57 // java/lang/System.out:Ljava/io/PrintStream;
#9 = Class #58 // java/lang/StringBuilder
#10 = Methodref #9.#59 // java/lang/StringBuilder."<init>":()V
#11 = Methodref #4.#60 // DAY.toString:()Ljava/lang/String;
#12 = Methodref #9.#61 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#13 = Methodref #9.#60 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#14 = Methodref #62.#63 // java/io/PrintStream.println:(Ljava/lang/String;)V
#15 = String #23 // DAY
#16 = String #64 // hello
#17 = Methodref #4.#65 // DAY."<init>":(Ljava/lang/String;ILjava/lang/String;)V
#18 = Fieldref #4.#66 // DAY.DAY:LDAY;
#19 = String #25 // NIGHT
#20 = String #67 // Good night
#21 = Fieldref #4.#68 // DAY.NIGHT:LDAY;
#22 = Class #69 // java/lang/Enum
#23 = Utf8 DAY
#24 = Utf8 LDAY;
#25 = Utf8 NIGHT
#26 = Utf8 arg
#27 = Utf8 Ljava/lang/String;
#28 = Utf8 a
#29 = Utf8 I
#30 = Utf8 ConstantValue
#31 = Integer 10
#32 = Utf8 $VALUES
#33 = Utf8 [LDAY;
#34 = Utf8 values
#35 = Utf8 ()[LDAY;
#36 = Utf8 Code
#37 = Utf8 LineNumberTable
#38 = Utf8 valueOf
#39 = Utf8 (Ljava/lang/String;)LDAY;
#40 = Utf8 <init>
#41 = Utf8 (Ljava/lang/String;ILjava/lang/String;)V
#42 = Utf8 Signature
#43 = Utf8 (Ljava/lang/String;)V
#44 = Utf8 print
#45 = Utf8 ()V
#46 = Utf8 <clinit>
#47 = Utf8 Ljava/lang/Enum<LDAY;>;
#48 = Utf8 SourceFile
#49 = Utf8 DAY.java
#50 = NameAndType #32:#33 // $VALUES:[LDAY;
#51 = Class #33 // "[LDAY;"
#52 = NameAndType #70:#71 // clone:()Ljava/lang/Object;
#53 = NameAndType #38:#72 // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
#54 = NameAndType #40:#73 // "<init>":(Ljava/lang/String;I)V
#55 = NameAndType #26:#27 // arg:Ljava/lang/String;
#56 = Class #74 // java/lang/System
#57 = NameAndType #75:#76 // out:Ljava/io/PrintStream;
#58 = Utf8 java/lang/StringBuilder
#59 = NameAndType #40:#45 // "<init>":()V
#60 = NameAndType #77:#78 // toString:()Ljava/lang/String;
#61 = NameAndType #79:#80 // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#62 = Class #81 // java/io/PrintStream
#63 = NameAndType #82:#43 // println:(Ljava/lang/String;)V
#64 = Utf8 hello
#65 = NameAndType #40:#41 // "<init>":(Ljava/lang/String;ILjava/lang/String;)V
#66 = NameAndType #23:#24 // DAY:LDAY;
#67 = Utf8 Good night
#68 = NameAndType #25:#24 // NIGHT:LDAY;
#69 = Utf8 java/lang/Enum
#70 = Utf8 clone
#71 = Utf8 ()Ljava/lang/Object;
#72 = Utf8 (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
#73 = Utf8 (Ljava/lang/String;I)V
#74 = Utf8 java/lang/System
#75 = Utf8 out
#76 = Utf8 Ljava/io/PrintStream;
#77 = Utf8 toString
#78 = Utf8 ()Ljava/lang/String;
#79 = Utf8 append
#80 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuilder;
#81 = Utf8 java/io/PrintStream
#82 = Utf8 println
{
public static final DAY DAY;
descriptor: LDAY;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
public static final DAY NIGHT;
descriptor: LDAY;
flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
private java.lang.String arg;
descriptor: Ljava/lang/String;
flags: ACC_PRIVATE
private static final int a;
descriptor: I
flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
ConstantValue: int 10
private static final DAY[] $VALUES;
descriptor: [LDAY;
flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
public static DAY[] values();
descriptor: ()[LDAY;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: getstatic #1 // Field $VALUES:[LDAY;
3: invokevirtual #2 // Method "[LDAY;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[LDAY;"
9: areturn
LineNumberTable:
line 1: 0
public static DAY valueOf(java.lang.String);
descriptor: (Ljava/lang/String;)LDAY;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: ldc #4 // class DAY
2: aload_0
3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #4 // class DAY
9: areturn
LineNumberTable:
line 1: 0
private DAY(java.lang.String);
descriptor: (Ljava/lang/String;ILjava/lang/String;)V
flags: ACC_PRIVATE
Code:
stack=3, locals=4, args_size=4
0: aload_0
1: aload_1
2: iload_2
3: invokespecial #6 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
6: aload_0
7: aload_3
8: putfield #7 // Field arg:Ljava/lang/String;
11: return
LineNumberTable:
line 9: 0
line 10: 6
line 11: 11
Signature: #43 // (Ljava/lang/String;)V
void print();
descriptor: ()V
flags:
Code:
stack=3, locals=1, args_size=1
0: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
3: new #9 // class java/lang/StringBuilder
6: dup
7: invokespecial #10 // Method java/lang/StringBuilder."<init>":()V
10: aload_0
11: invokevirtual #11 // Method toString:()Ljava/lang/String;
14: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: aload_0
18: getfield #7 // Field arg:Ljava/lang/String;
21: invokevirtual #12 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #13 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: invokevirtual #14 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: return
LineNumberTable:
line 14: 0
line 15: 30
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=5, locals=0, args_size=0
0: new #4 // class DAY
3: dup
4: ldc #15 // String DAY
6: iconst_0
7: ldc #16 // String hello
9: invokespecial #17 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
12: putstatic #18 // Field DAY:LDAY;
15: new #4 // class DAY
18: dup
19: ldc #19 // String NIGHT
21: iconst_1
22: ldc #20 // String Good night
24: invokespecial #17 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
27: putstatic #21 // Field NIGHT:LDAY;
30: iconst_2
31: anewarray #4 // class DAY
34: dup
35: iconst_0
36: getstatic #18 // Field DAY:LDAY;
39: aastore
40: dup
41: iconst_1
42: getstatic #21 // Field NIGHT:LDAY;
45: aastore
46: putstatic #1 // Field $VALUES:[LDAY;
49: return
LineNumberTable:
line 2: 0
line 3: 15
line 1: 30
}
Signature: #47 // Ljava/lang/Enum<LDAY;>;
SourceFile: "DAY.java"
正常的java代码为:
public final class DAY extends java.lang.Enum<DAY> {
public static final DAY DAY;
public static final DAY NIGHT;
private String arg;
private static final int a = 10;
private static final DAY[] $VALUES;
public static DAY[] values() {
return $VALUES.clone();
}
public static DAY valueOf(String name) {
return (DAY) Enum.valueOf(DAY.class, name);
}
private DAY(String name, int seq, String arg) {
super(name, seq);
this.arg = arg;
}
void print() {
System.out.println(toString() + arg);
}
static {
DAY = new DAY("DAY", 0, "hello");
NIGHT = new DAY("NIGHT", 1, "Good night");
$VALUES = new DAY[2];
$VALUES[0] = DAY;
$VALUES[1] = NIGHT;
}
}
枚举用途
利用枚举实现常量和方法的绑定
在实际编程任务中,经常会遇到需要将特定方法和常量值进行绑定的程序,如一个计算器类,可能有如下形式:
public class Operation {
public static final int ADD = 1;
public static final int MINUS = 2;
public static final int TIMES = 3;
public static final int DIVIDE = 4;
public Operation(int type) {
this.type = type;
}
private int type;
double apply(double x, double y) {
switch(type) {
case ADD: return x + y;
case MINUS: return x - y;
case TIMES: return x * y;
case DIVIDE: return x / y;
}
throw new AssertionError("unknow op:" + type);
}
}
这可能是一个比较正常的编程写法,但是有个原则是尽量避免int常量,我们将这个程序更改成枚举类型以避免使用int常量,程序代码如下:
public enum Operation {
ADD, MINUS, TIMES, DIVIDE;
double apply(double x, double y) {
switch(this) {
case ADD: return x + y;
case MINUS: return x - y;
case TIMES: return x * y;
case DIVIDE: return x / y;
}
throw new AssertionError("unknow op:" + this);
}
}
以上避免了int常量的书写,但是这个写法是由switch控制的,假设我们想增加一个平方的静态变量,却又忘记了增加switch的分枝语句,就会出现意想不到的问题。我们可以利用方法保证每个枚举类型都必须实现自己的apply方法。
public enum Operation {
ADD {@Override double apply(double x, double y) { return x + y;} },
MINUS {@Override double apply(double x, double y) { return x - y;} },
TIMES {@Override double apply(double x, double y) { return x * y;} },
DIVIDE {@Override double apply(double x, double y) { return x / y;} };
abstract apply(double x, double y);
}
EnumSet
EnumSet是利用位保存常量的高速set结构。EumSet的源码如下
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, java.io.Serializable
{
//存储Enum类型
final Class<E> elementType;
//缓存所有当前枚举的所有常量
final Enum<?>[] universe;
private static Enum<?>[] ZERO_LENGTH_ENUM_ARRAY = new Enum<?>[0];
EnumSet(Class<E>elementType, Enum<?>[] universe) {
this.elementType = elementType;
this.universe = universe;
}
//创建一个空的set
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);
}
//创建一个包含所有枚举的set
public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) {
EnumSet<E> result = noneOf(elementType);
result.addAll();
return result;
}
abstract void addAll();
public static <E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s) {
return s.clone();
}
//创建一个包含C的枚举,其中c必须继承Enum
public static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c) {
if (c instanceof EnumSet) {
return ((EnumSet<E>)c).clone();
} else {
if (c.isEmpty())
throw new IllegalArgumentException("Collection is empty");
Iterator<E> i = c.iterator();
E first = i.next();
EnumSet<E> result = EnumSet.of(first);
while (i.hasNext())
result.add(i.next());
return result;
}
}
//创建一补集枚举set
public static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s) {
EnumSet<E> result = copyOf(s);
result.complement();
return result;
}
//创建一个包含e的set
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> of(E e1, E e2) {
EnumSet<E> result = noneOf(e1.getDeclaringClass());
result.add(e1);
result.add(e2);
return result;
}
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3) {
EnumSet<E> result = noneOf(e1.getDeclaringClass());
result.add(e1);
result.add(e2);
result.add(e3);
return result;
}
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4) {
EnumSet<E> result = noneOf(e1.getDeclaringClass());
result.add(e1);
result.add(e2);
result.add(e3);
result.add(e4);
return result;
}
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4,
E e5)
{
EnumSet<E> result = noneOf(e1.getDeclaringClass());
result.add(e1);
result.add(e2);
result.add(e3);
result.add(e4);
result.add(e5);
return result;
}
@SafeVarargs
public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest) {
EnumSet<E> result = noneOf(first.getDeclaringClass());
result.add(first);
for (E e : rest)
result.add(e);
return result;
}
public static <E extends Enum<E>> EnumSet<E> range(E from, E to) {
if (from.compareTo(to) > 0)
throw new IllegalArgumentException(from + " > " + to);
EnumSet<E> result = noneOf(from.getDeclaringClass());
result.addRange(from, to);
return result;
}
abstract void addRange(E from, E to);
@SuppressWarnings("unchecked")
public EnumSet<E> clone() {
try {
return (EnumSet<E>) super.clone();
} catch(CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
abstract void complement();
final void typeCheck(E e) {
Class<?> eClass = e.getClass();
if (eClass != elementType && eClass.getSuperclass() != elementType)
throw new ClassCastException(eClass + " != " + elementType);
}
private static <E extends Enum<E>> E[] getUniverse(Class<E> elementType) {
return SharedSecrets.getJavaLangAccess()
.getEnumConstantsShared(elementType);
}
private static class SerializationProxy <E extends Enum<E>>
implements java.io.Serializable
{
/**
* The element type of this enum set.
*
* @serial
*/
private final Class<E> elementType;
/**
* The elements contained in this enum set.
*
* @serial
*/
private final Enum<?>[] elements;
SerializationProxy(EnumSet<E> set) {
elementType = set.elementType;
elements = set.toArray(ZERO_LENGTH_ENUM_ARRAY);
}
// instead of cast to E, we should perhaps use elementType.cast()
// to avoid injection of forged stream, but it will slow the implementation
@SuppressWarnings("unchecked")
private Object readResolve() {
EnumSet<E> result = EnumSet.noneOf(elementType);
for (Enum<?> e : elements)
result.add((E)e);
return result;
}
private static final long serialVersionUID = 362491234563181265L;
}
Object writeReplace() {
return new SerializationProxy<>(this);
}
// readObject method for the serialization proxy pattern
// See Effective Java, Second Ed., Item 78.
private void readObject(java.io.ObjectInputStream stream)
throws java.io.InvalidObjectException {
throw new java.io.InvalidObjectException("Proxy required");
}
}
可以看到,EnumSet是一个抽象类,只能调用静态方法来创建类。其中,最重要的静态方法是
noneOf(Class<E> elementType)
,这个静态方法依据长度返回两个EnumSet的子类(长度即枚举常量的个数),我们先来看RegularEnumSet
是怎么实现按位存取的。
class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
private static final long serialVersionUID = 3411599620347842686L;
//代表所存的常量有哪些,2^k代表Enum[k]在这个set中,换句话说,long的64位中哪一位置一,那么对应位数的枚举常量就在set中。
private long elements = 0L;
RegularEnumSet(Class<E>elementType, Enum<?>[] universe) {
super(elementType, universe);
}
void addRange(E from, E to) {
//-1L = 0xFFFFFFFFFFFFFFFF
// 先无符号右移(>>>)(from.ordinal() - to.ordinal() - 1)位, 得到总共有多少个元素被加入,然后左移(from.ordinal())位,得到起始位置
elements = (-1L >>> (from.ordinal() - to.ordinal() - 1)) << from.ordinal();
}
void addAll() {
//枚举常量有多少个,就将前多少位置1
if (universe.length != 0)
elements = -1L >>> -universe.length;
}
void complement() {
if (universe.length != 0) {
//取有效位的反
elements = ~elements;
elements &= -1L >>> -universe.length; // Mask unused bits
}
}
public Iterator<E> iterator() {
return new EnumSetIterator<>();
}
private class EnumSetIterator<E extends Enum<E>> implements Iterator<E> {
/**
* A bit vector representing the elements in the set not yet
* returned by this iterator.
*/
long unseen;
/**
* The bit representing the last element returned by this iterator
* but not removed, or zero if no such element exists.
*/
long lastReturned = 0;
EnumSetIterator() {
unseen = elements;
}
public boolean hasNext() {
return unseen != 0;
}
@SuppressWarnings("unchecked")
public E next() {
if (unseen == 0)
throw new NoSuchElementException();
//得到只有最低位为1的数
lastReturned = unseen & -unseen;
unseen -= lastReturned;
return (E) universe[Long.numberOfTrailingZeros(lastReturned)];
}
public void remove() {
if (lastReturned == 0)
throw new IllegalStateException();
elements &= ~lastReturned;
lastReturned = 0;
}
}
//统计元素个数
public int size() {
return Long.bitCount(elements);
}
public boolean isEmpty() {
return elements == 0;
}
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;
}
public boolean add(E e) {
typeCheck(e);
long oldElements = elements;
elements |= (1L << ((Enum<?>)e).ordinal());
return elements != oldElements;
}
public boolean remove(Object e) {
if (e == null)
return false;
Class<?> eClass = e.getClass();
if (eClass != elementType && eClass.getSuperclass() != elementType)
return false;
long oldElements = elements;
elements &= ~(1L << ((Enum<?>)e).ordinal());
return elements != oldElements;
}
public boolean containsAll(Collection<?> c) {
if (!(c instanceof RegularEnumSet))
return super.containsAll(c);
RegularEnumSet<?> es = (RegularEnumSet<?>)c;
if (es.elementType != elementType)
return es.isEmpty();
return (es.elements & ~elements) == 0;
}
public boolean addAll(Collection<? extends E> c) {
if (!(c instanceof RegularEnumSet))
return super.addAll(c);
RegularEnumSet<?> es = (RegularEnumSet<?>)c;
if (es.elementType != elementType) {
if (es.isEmpty())
return false;
else
throw new ClassCastException(
es.elementType + " != " + elementType);
}
long oldElements = elements;
elements |= es.elements;
return elements != oldElements;
}
public boolean removeAll(Collection<?> c) {
if (!(c instanceof RegularEnumSet))
return super.removeAll(c);
RegularEnumSet<?> es = (RegularEnumSet<?>)c;
if (es.elementType != elementType)
return false;
long oldElements = elements;
elements &= ~es.elements;
return elements != oldElements;
}
public boolean retainAll(Collection<?> c) {
if (!(c instanceof RegularEnumSet))
return super.retainAll(c);
RegularEnumSet<?> es = (RegularEnumSet<?>)c;
if (es.elementType != elementType) {
boolean changed = (elements != 0);
elements = 0;
return changed;
}
long oldElements = elements;
elements &= es.elements;
return elements != oldElements;
}
public void clear() {
elements = 0;
}
public boolean equals(Object o) {
if (!(o instanceof RegularEnumSet))
return super.equals(o);
RegularEnumSet<?> es = (RegularEnumSet<?>)o;
if (es.elementType != elementType)
return elements == 0 && es.elements == 0;
return es.elements == elements;
}
}
Note
- 在
RegularEnumSet
中,使用long来保存有多少个常量,long总共有64位,那么这个类能够保存的常量个数就是64。- 左移和右移的有操作数和左操作数有关,如果左操作数只有32位,那么有操作数的前5位有效,如果左操作数64位,则有操作数的前6位有效。
最后,我们看JumboEnumSet
如何实现按位存储。
//部分代码如下
class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> {
private static final long serialVersionUID = 334349849919042784L;
//利用多个long数组保存位信息
private long elements[];
private int size = 0;
JumboEnumSet(Class<E>elementType, Enum<?>[] universe) {
super(elementType, universe);
//根据枚举的最多个数求解elements的长度
elements = new long[(universe.length + 63) >>> 6];
}
void addRange(E from, E to) {
int fromIndex = from.ordinal() >>> 6;
int toIndex = to.ordinal() >>> 6;
if (fromIndex == toIndex) {
elements[fromIndex] = (-1L >>> (from.ordinal() - to.ordinal() - 1))
<< from.ordinal();
} else {
elements[fromIndex] = (-1L << from.ordinal());
for (int i = fromIndex + 1; i < toIndex; i++)
elements[i] = -1;
elements[toIndex] = -1L >>> (63 - to.ordinal());
}
size = to.ordinal() - from.ordinal() + 1;
}
void addAll() {
for (int i = 0; i < elements.length; i++)
elements[i] = -1;
elements[elements.length - 1] >>>= -universe.length;
size = universe.length;
}
void complement() {
for (int i = 0; i < elements.length; i++)
elements[i] = ~elements[i];
elements[elements.length - 1] &= (-1L >>> -universe.length);
size = universe.length - size;
}
public Iterator<E> iterator() {
return new EnumSetIterator<>();
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
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;
}
public boolean add(E e) {
typeCheck(e);
int eOrdinal = e.ordinal();
int eWordNum = eOrdinal >>> 6;
long oldElements = elements[eWordNum];
elements[eWordNum] |= (1L << eOrdinal);
boolean result = (elements[eWordNum] != oldElements);
if (result)
size++;
return result;
}
public boolean remove(Object e) {
if (e == null)
return false;
Class<?> eClass = e.getClass();
if (eClass != elementType && eClass.getSuperclass() != elementType)
return false;
int eOrdinal = ((Enum<?>)e).ordinal();
int eWordNum = eOrdinal >>> 6;
long oldElements = elements[eWordNum];
elements[eWordNum] &= ~(1L << eOrdinal);
boolean result = (elements[eWordNum] != oldElements);
if (result)
size--;
return result;
}
public boolean containsAll(Collection<?> c) {
if (!(c instanceof JumboEnumSet))
return super.containsAll(c);
JumboEnumSet<?> es = (JumboEnumSet<?>)c;
if (es.elementType != elementType)
return es.isEmpty();
for (int i = 0; i < elements.length; i++)
if ((es.elements[i] & ~elements[i]) != 0)
return false;
return true;
}
public boolean addAll(Collection<? extends E> c) {
if (!(c instanceof JumboEnumSet))
return super.addAll(c);
JumboEnumSet<?> es = (JumboEnumSet<?>)c;
if (es.elementType != elementType) {
if (es.isEmpty())
return false;
else
throw new ClassCastException(
es.elementType + " != " + elementType);
}
for (int i = 0; i < elements.length; i++)
elements[i] |= es.elements[i];
return recalculateSize();
}
public boolean removeAll(Collection<?> c) {
if (!(c instanceof JumboEnumSet))
return super.removeAll(c);
JumboEnumSet<?> es = (JumboEnumSet<?>)c;
if (es.elementType != elementType)
return false;
for (int i = 0; i < elements.length; i++)
elements[i] &= ~es.elements[i];
return recalculateSize();
}
public boolean retainAll(Collection<?> c) {
if (!(c instanceof JumboEnumSet))
return super.retainAll(c);
JumboEnumSet<?> es = (JumboEnumSet<?>)c;
if (es.elementType != elementType) {
boolean changed = (size != 0);
clear();
return changed;
}
for (int i = 0; i < elements.length; i++)
elements[i] &= es.elements[i];
return recalculateSize();
}
public void clear() {
Arrays.fill(elements, 0);
size = 0;
}
public boolean equals(Object o) {
if (!(o instanceof JumboEnumSet))
return super.equals(o);
JumboEnumSet<?> es = (JumboEnumSet<?>)o;
if (es.elementType != elementType)
return size == 0 && es.size == 0;
return Arrays.equals(es.elements, elements);
}
private boolean recalculateSize() {
int oldSize = size;
size = 0;
for (long elt : elements)
size += Long.bitCount(elt);
return size != oldSize;
}
public EnumSet<E> clone() {
JumboEnumSet<E> result = (JumboEnumSet<E>) super.clone();
result.elements = result.elements.clone();
return result;
EnumMap
EnumMap是键为Enum的特殊的map,这种限制导致了map的大小最多为Enum的常量的个数,EnumMap的源码如下:
public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
implements java.io.Serializable, Cloneable
{
//枚举的类型
private final Class<K> keyType;
//缓存所有枚举的常量
private transient K[] keyUniverse;
//利用数组存储value,比如,数组为1的存储ordinal()为1的键的值
private transient Object[] vals;
private transient int size = 0;
//区分非空值的NULL
private static final Object NULL = new Object() {
public int hashCode() {
return 0;
}
public String toString() {
return "java.util.EnumMap.NULL";
}
};
//用作将null映射成NULL
private Object maskNull(Object value) {
return (value == null ? NULL : value);
}
@SuppressWarnings("unchecked")
private V unmaskNull(Object value) {
return (V)(value == NULL ? null : value);
}
private static final Enum<?>[] ZERO_LENGTH_ENUM_ARRAY = new Enum<?>[0];
//创建一个空map
public EnumMap(Class<K> keyType) {
this.keyType = keyType;
keyUniverse = getKeyUniverse(keyType);
vals = new Object[keyUniverse.length];
}
public EnumMap(EnumMap<K, ? extends V> m) {
keyType = m.keyType;
keyUniverse = m.keyUniverse;
vals = m.vals.clone();
size = m.size;
}
public EnumMap(Map<K, ? extends V> m) {
if (m instanceof EnumMap) {
EnumMap<K, ? extends V> em = (EnumMap<K, ? extends V>) m;
keyType = em.keyType;
keyUniverse = em.keyUniverse;
vals = em.vals.clone();
size = em.size;
} else {
if (m.isEmpty())
throw new IllegalArgumentException("Specified map is empty");
keyType = m.keySet().iterator().next().getDeclaringClass();
keyUniverse = getKeyUniverse(keyType);
vals = new Object[keyUniverse.length];
putAll(m);
}
}
public int size() {
return size;
}
//因为null会被映射成NULL,所以空值也可能有结果
public boolean containsValue(Object value) {
value = maskNull(value);
for (Object val : vals)
if (value.equals(val))
return true;
return false;
}
//检查是否有这个key,key存在的条件是key属于对应的枚举且枚举序号对应的数组不为空
public boolean containsKey(Object key) {
return isValidKey(key) && vals[((Enum<?>)key).ordinal()] != null;
}
//检查是否有这个map对,条件是key值有效并且value相同
private boolean containsMapping(Object key, Object value) {
return isValidKey(key) &&
maskNull(value).equals(vals[((Enum<?>)key).ordinal()]);
}
//返回value,如果value是空值存入,则返回null
public V get(Object key) {
return (isValidKey(key) ?
unmaskNull(vals[((Enum<?>)key).ordinal()]) : null);
}
public V put(K key, V value) {
typeCheck(key);
int index = key.ordinal();
Object oldValue = vals[index];
vals[index] = maskNull(value);
if (oldValue == null)
size++;
return unmaskNull(oldValue);
}
public V remove(Object key) {
if (!isValidKey(key))
return null;
int index = ((Enum<?>)key).ordinal();
Object oldValue = vals[index];
vals[index] = null;
if (oldValue != null)
size--;
return unmaskNull(oldValue);
}
private boolean removeMapping(Object key, Object value) {
if (!isValidKey(key))
return false;
int index = ((Enum<?>)key).ordinal();
if (maskNull(value).equals(vals[index])) {
vals[index] = null;
size--;
return true;
}
return false;
}
//测试键值是否为有效键值,有效键值为当前枚举类或其子类
private boolean isValidKey(Object key) {
if (key == null)
return false;
// Cheaper than instanceof Enum followed by getDeclaringClass
Class<?> keyClass = key.getClass();
return keyClass == keyType || keyClass.getSuperclass() == keyType;
}
// Bulk Operations
public void putAll(Map<? extends K, ? extends V> m) {
if (m instanceof EnumMap) {
EnumMap<?, ?> em = (EnumMap<?, ?>)m;
if (em.keyType != keyType) {
if (em.isEmpty())
return;
throw new ClassCastException(em.keyType + " != " + keyType);
}
for (int i = 0; i < keyUniverse.length; i++) {
Object emValue = em.vals[i];
if (emValue != null) {
if (vals[i] == null)
size++;
vals[i] = emValue;
}
}
} else {
super.putAll(m);
}
}
public void clear() {
Arrays.fill(vals, null);
size = 0;
}
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new KeySet();
keySet = ks;
}
return ks;
}
private class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return new KeyIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
int oldSize = size;
EnumMap.this.remove(o);
return size != oldSize;
}
public void clear() {
EnumMap.this.clear();
}
}
public Collection<V> values() {
Collection<V> vs = values;
if (vs == null) {
vs = new Values();
values = vs;
}
return vs;
}
private class Values extends AbstractCollection<V> {
public Iterator<V> iterator() {
return new ValueIterator();
}
public int size() {
return size;
}
public boolean contains(Object o) {
return containsValue(o);
}
public boolean remove(Object o) {
o = maskNull(o);
for (int i = 0; i < vals.length; i++) {
if (o.equals(vals[i])) {
vals[i] = null;
size--;
return true;
}
}
return false;
}
public void clear() {
EnumMap.this.clear();
}
}
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es = entrySet;
if (es != null)
return es;
else
return entrySet = new EntrySet();
}
private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
return containsMapping(entry.getKey(), entry.getValue());
}
public boolean remove(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> entry = (Map.Entry<?,?>)o;
return removeMapping(entry.getKey(), entry.getValue());
}
public int size() {
return size;
}
public void clear() {
EnumMap.this.clear();
}
public Object[] toArray() {
return fillEntryArray(new Object[size]);
}
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size)
a = (T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
if (a.length > size)
a[size] = null;
return (T[]) fillEntryArray(a);
}
private Object[] fillEntryArray(Object[] a) {
int j = 0;
for (int i = 0; i < vals.length; i++)
if (vals[i] != null)
a[j++] = new AbstractMap.SimpleEntry<>(
keyUniverse[i], unmaskNull(vals[i]));
return a;
}
}
private abstract class EnumMapIterator<T> implements Iterator<T> {
// Lower bound on index of next element to return
int index = 0;
// Index of last returned element, or -1 if none
int lastReturnedIndex = -1;
public boolean hasNext() {
while (index < vals.length && vals[index] == null)
index++;
return index != vals.length;
}
public void remove() {
checkLastReturnedIndex();
if (vals[lastReturnedIndex] != null) {
vals[lastReturnedIndex] = null;
size--;
}
lastReturnedIndex = -1;
}
private void checkLastReturnedIndex() {
if (lastReturnedIndex < 0)
throw new IllegalStateException();
}
}
private class KeyIterator extends EnumMapIterator<K> {
public K next() {
if (!hasNext())
throw new NoSuchElementException();
lastReturnedIndex = index++;
return keyUniverse[lastReturnedIndex];
}
}
private class ValueIterator extends EnumMapIterator<V> {
public V next() {
if (!hasNext())
throw new NoSuchElementException();
lastReturnedIndex = index++;
return unmaskNull(vals[lastReturnedIndex]);
}
}
private class EntryIterator extends EnumMapIterator<Map.Entry<K,V>> {
private Entry lastReturnedEntry;
public Map.Entry<K,V> next() {
if (!hasNext())
throw new NoSuchElementException();
lastReturnedEntry = new Entry(index++);
return lastReturnedEntry;
}
public void remove() {
lastReturnedIndex =
((null == lastReturnedEntry) ? -1 : lastReturnedEntry.index);
super.remove();
lastReturnedEntry.index = lastReturnedIndex;
lastReturnedEntry = null;
}
private class Entry implements Map.Entry<K,V> {
private int index;
private Entry(int index) {
this.index = index;
}
public K getKey() {
checkIndexForEntryUse();
return keyUniverse[index];
}
public V getValue() {
checkIndexForEntryUse();
return unmaskNull(vals[index]);
}
public V setValue(V value) {
checkIndexForEntryUse();
V oldValue = unmaskNull(vals[index]);
vals[index] = maskNull(value);
return oldValue;
}
public boolean equals(Object o) {
if (index < 0)
return o == this;
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
V ourValue = unmaskNull(vals[index]);
Object hisValue = e.getValue();
return (e.getKey() == keyUniverse[index] &&
(ourValue == hisValue ||
(ourValue != null && ourValue.equals(hisValue))));
}
public int hashCode() {
if (index < 0)
return super.hashCode();
return entryHashCode(index);
}
public String toString() {
if (index < 0)
return super.toString();
return keyUniverse[index] + "="
+ unmaskNull(vals[index]);
}
private void checkIndexForEntryUse() {
if (index < 0)
throw new IllegalStateException("Entry was removed");
}
}
}
public boolean equals(Object o) {
if (this == o)
return true;
if (o instanceof EnumMap)
return equals((EnumMap<?,?>)o);
if (!(o instanceof Map))
return false;
Map<?,?> m = (Map<?,?>)o;
if (size != m.size())
return false;
for (int i = 0; i < keyUniverse.length; i++) {
if (null != vals[i]) {
K key = keyUniverse[i];
V value = unmaskNull(vals[i]);
if (null == value) {
if (!((null == m.get(key)) && m.containsKey(key)))
return false;
} else {
if (!value.equals(m.get(key)))
return false;
}
}
}
return true;
}
private boolean equals(EnumMap<?,?> em) {
if (em.keyType != keyType)
return size == 0 && em.size == 0;
// Key types match, compare each value
for (int i = 0; i < keyUniverse.length; i++) {
Object ourValue = vals[i];
Object hisValue = em.vals[i];
if (hisValue != ourValue &&
(hisValue == null || !hisValue.equals(ourValue)))
return false;
}
return true;
}
public int hashCode() {
int h = 0;
for (int i = 0; i < keyUniverse.length; i++) {
if (null != vals[i]) {
h += entryHashCode(i);
}
}
return h;
}
private int entryHashCode(int index) {
return (keyUniverse[index].hashCode() ^ vals[index].hashCode());
}
@SuppressWarnings("unchecked")
public EnumMap<K, V> clone() {
EnumMap<K, V> result = null;
try {
result = (EnumMap<K, V>) super.clone();
} catch(CloneNotSupportedException e) {
throw new AssertionError();
}
result.vals = result.vals.clone();
result.entrySet = null;
return result;
}
private void typeCheck(K key) {
Class<?> keyClass = key.getClass();
if (keyClass != keyType && keyClass.getSuperclass() != keyType)
throw new ClassCastException(keyClass + " != " + keyType);
}
private static <K extends Enum<K>> K[] getKeyUniverse(Class<K> keyType) {
return SharedSecrets.getJavaLangAccess()
.getEnumConstantsShared(keyType);
}
private static final long serialVersionUID = 458661240069192865L;
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException
{
// Write out the key type and any hidden stuff
s.defaultWriteObject();
// Write out size (number of Mappings)
s.writeInt(size);
// Write out keys and values (alternating)
int entriesToBeWritten = size;
for (int i = 0; entriesToBeWritten > 0; i++) {
if (null != vals[i]) {
s.writeObject(keyUniverse[i]);
s.writeObject(unmaskNull(vals[i]));
entriesToBeWritten--;
}
}
}
@SuppressWarnings("unchecked")
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException
{
// Read in the key type and any hidden stuff
s.defaultReadObject();
keyUniverse = getKeyUniverse(keyType);
vals = new Object[keyUniverse.length];
// Read in size (number of Mappings)
int size = s.readInt();
// Read the keys and values, and put the mappings in the HashMap
for (int i = 0; i < size; i++) {
K key = (K) s.readObject();
V value = (V) s.readObject();
put(key, value);
}
}
}
网友评论