美文网首页
JAVA-枚举

JAVA-枚举

作者: sschrodinger | 来源:发表于2019-03-08 20:03 被阅读0次

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;
    }
}

可以看到,编译器自动将我们的枚举类型转化成了一个继承了 Enumfinal 类。我们先来看看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);
        }
    }
}

相关文章

  • JAVA-枚举

    JAVA-枚举 sschrodinger 2018/11/5 枚举基本形式 枚举的基本形式如上所示,基本可以看出,...

  • Java-枚举

    一、枚举类的理解 当需要定义一组常量时,强烈建议使用枚举类JDK1.5之前需要自定义枚举类,JDK1.5新增的en...

  • Java-枚举类型 (28)

    这里创建了一个名为Spiciness的枚举类型,它具有5个具名值,由于枚举类型的实例是常量,因此按照命名惯例它们都...

  • Hello Java

    目录 Java-基础(1/6) Java-对象(2/6) Java-核心库类 上(3/6) Java-核心库类下(...

  • Effective Java-枚举和注解

    枚举和注解是Java1.5版本中新增的特性,本章讨论使用它们时的最佳实践。本章内容导图: 1.用enum代替int...

  • Effective Java-枚举和注释

    用enum代替int常量 用实例域代替序数 永远不要根据枚举的序数导出与它相关联的值,而是要将它保存在一个实例域中...

  • [Java]重学Java-枚举类

    枚举可以做什么 假设我们现在想表达星期,为了避免魔法值的出现,我们可能会写一个常量类: 这样一来,1-7的星期数就...

  • Java-浅析Object类

    Java-浅析Object类 ++2016.7.19++byside @Java-浅析Object类 ======...

  • JAVA-线程-一-执行器Executor

    2. JAVA-线程-二-ExecutorService 接口 3. JAVA-线程-三-AbstractExec...

  • JAVA-线程-三-AbstractExecutorServic

    1. JAVA-线程-一-执行器Executor 2. JAVA-线程-二-ExecutorService 接口 ...

网友评论

      本文标题:JAVA-枚举

      本文链接:https://www.haomeiwen.com/subject/cyudpqtx.html