java集合-EnumMap与EnumSet

一:java枚举简介

1:基本概念:

  • 用enum定义枚举类默认继承了java.lang.Enum类而不是继承了Object类。其中java.lang.Enum类实现了java.lang.Serializable和java.lang.Comparable两个接口
  • 枚举类的构造函数只能使用private访问修饰符,如果省略了其构造器的访问控制符,则默认使用private修饰
  • 枚举类的所有实例必须在枚举类中显式列出,否则这个枚举类将永远都不能产生实例。列出这些实例时,系统会自动添加public static final修饰,无需程序员显式添加。

    2:基本用法:

    用法一:常量

    之前我们使用常量都是定义为public static final XXX,现在我们可以用它

public enum Color {  
  RED, GREEN, BLANK, YELLOW  
} 

用法二:switch

enum Signal {  
    GREEN, YELLOW, RED  
}  
public class TrafficLight {  
    Signal color = Signal.RED;  
    public void change() {  
        switch (color) {  
        case RED:  
            color = Signal.GREEN;  
            break;  
        case YELLOW:  
            color = Signal.RED;  
            break;  
        case GREEN:  
            color = Signal.YELLOW;  
            break;  
        }  
    }  
}  

用法三:向枚举中添加新方法

public enum Color {  
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  
    // 成员变量  
    private String name;  
    private int index;  
    // 构造方法  
    private Color(String name, int index) {  
        this.name = name;  
        this.index = index;  
    }  
    // 普通方法  
    public static String getName(int index) {  
        for (Color c : Color.values()) {  
            if (c.getIndex() == index) {  
                return c.name;  
            }  
        }  
        return null;  
    }  
    // get set 方法  
    public String getName() {  
        return name;  
    }  
    public void setName(String name) {  
        this.name = name;  
    }  
    public int getIndex() {  
        return index;  
    }  
    public void setIndex(int index) {  
        this.index = index;  
    }  
}  


测试端:

public class EnumTest {

        public static void main(String[] args) {
                // TODO Auto-generated method stub
                
                System.out.println(Color.GREEN.getName());
                System.out.println(Color.GREEN.getIndex());
                System.out.println(Color.GREEN.toString());
                

        }

}

用法四:覆盖枚举的方法

public enum Color {  
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  
    // 成员变量  
    private String name;  
    private int index;  
    // 构造方法  
    private Color(String name, int index) {  
        this.name = name;  
        this.index = index;  
    }  
    //覆盖方法  
    @Override  
    public String toString() {  
        return this.index+"_"+this.name;  
    }  
}  

用法五:实现接口

所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。

public interface Behaviour {  
    void print();  
    String getInfo();  
}  
public enum Color implements Behaviour{  
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  
    // 成员变量  
    private String name;  
    private int index;  
    // 构造方法  
    private Color(String name, int index) {  
        this.name = name;  
        this.index = index;  
    }  
//接口方法  
    @Override  
    public String getInfo() {  
        return this.name;  
    }  
    //接口方法  
    @Override  
    public void print() {  
        System.out.println(this.index+":"+this.name);  
    }  
}  

用法六:使用接口组织枚举

public interface Food {
        enum Coffee implements Food{  
        BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO  
    }  
    enum Dessert implements Food{  
        FRUIT, CAKE, GELATO  
    } 

}

EnumMap基本用法

public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
    implements java.io.Serializable, Cloneable

从上述定义可以看出,EnumMap的Key,必须事Enum 类型,而Value可以是任意类型。看下边初始化格式:

EnumMap<Color, String> enumMap = new EnumMap<Color, String>(Color.class);

实例如下:

import java.util.EnumMap;
import java.util.Iterator;
import java.util.Map.Entry;

public class EnumTest {

        public static void main(String[] args) {
                // TODO Auto-generated method stub
                
                EnumMap<Color, String> enumMap=new EnumMap<>(Color.class);
                enumMap.put(Color.BLANK, "黑夜就是我的面纱");
                enumMap.put(Color.GREEN, "我就给你绿帽子");
                enumMap.put(Color.RED, "红旗我的样子");
                enumMap.put(Color.BLANK, "黑夜没月亮");
                Iterator<Entry<Color, String>> iterator=enumMap.entrySet().iterator();
                while(iterator.hasNext())
                {
                        System.out.println(iterator.next());
                }
                

        }

}

运行结果:

RED=红旗我的样子
GREEN=我就给你绿帽子
BLANK=黑夜没月亮

从实例我们发现,可以通过这种方式给枚举赋上特殊的值。

EnumSet基本用法

  • EnumSet 是一个与枚举类型一起使用的专用 Set 实现。枚举set中所有元素都必须来自单个枚举类型(即必须是同类型,且该类型是Enum的子类)。枚举类型在创建 set 时显式或隐式地指定。枚举 set 在内部表示为位向量。 此表示形式非常紧凑且高效。此类的空间和时间性能应该很好,足以用作传统上基于 int 的“位标志”的替换形式,具有高品质、类型安全的优势。

  • Enumset是个虚类,我们只能通过它提供的静态方法来返回Enumset的实现类的实例。返回EnumSet的两种不同的实现:如果EnumSet大小小于64,就返回RegularEnumSet实例(当然它继承自EnumSet),这个EnumSet实际上至用了一个long来存储这个EnumSet。如果 EnumSet大小大于等于64,则返回JumboEnumSet实例,它使用一个long[]来存储。这样做的好处很明显: 大多数情况下返回的RegularEnumSet效率比JumboEnumSet高很多。

    源码:

public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
        Enum<?>[] universe = getUniverse(elementType);
        if (universe == null)
            throw new ClassCastException(elementType + " not an enum");

        if (universe.length <= 64)
            return new RegularEnumSet<>(elementType, universe);
        else
            return new JumboEnumSet<>(elementType, universe);
    }

上文说RegularEnumSet方法效率高很多,那为什么呢 ,我们看看RegularEnumSet的源码:

枚举存储:

private long elements = 0L;

add方法:

public boolean add(E e) {
        typeCheck(e);

        long oldElements = elements;
        elements |= (1L << ((Enum<?>)e).ordinal());
        return elements != oldElements;
    }

细心的读者发现,小于64位时,仅仅是对一个长整型数据进行操作,效率当然要快许多。

我们再看看JumboEnumSet类实现:

枚举值存储:

private long elements[];
 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;
    }

从源码看出,JumboEnumSet使用long数组存储数据,添加操作也是操作数组,效率当然要慢点。

实例:

import java.util.EnumSet;
import java.util.Iterator;

public class EnumSetTest {

        public static void main(String[] args) {
                // TODO Auto-generated method stub
        EnumSet<Color> enumSet=EnumSet.allOf(Color.class);
        Iterator<Color> iterator=enumSet.iterator();
        while (iterator.hasNext()) {
                        System.out.println(iterator.next());
                        
                }
        }

}

运行结果:

RED
GREEN
BLANK
YELLO

综合实例(线程状态)

public enum ThreadStates {
        START,
        RUNNING,
        WAITING,
        DEAD;
}
import java.io.Closeable;
import java.io.IOException;

/**
 * This Enum example shows all the things we can do with Enum types
 * @author pankaj
 *
 */
public enum ThreadStatesEnum implements Closeable{
        START(1){
                @Override
                public String toString(){
                        return "START implementation. Priority="+getPriority();
                }

                @Override
                public String getDetail() {
                        return "START";
                }
        },
        RUNNING(2){
                @Override
                public String getDetail() {
                        return "RUNNING";
                }
        },
        WAITING(3){
                @Override
                public String getDetail() {
                        return "WAITING";
                }
        },
        DEAD(4){
                @Override
                public String getDetail() {
                        return "DEAD";
                }
        };
        
        private int priority;
        
        public abstract String getDetail();
        //Enum constructors should always be private.
        private ThreadStatesEnum(int i){
                priority = i;
        }
        
        //Enum can have methods
        public int getPriority(){
                return this.priority;
        }
        
        public void setPriority(int p){
                this.priority = p;
        }
        
        //Enum can override functions
        @Override
        public String toString(){
                return "Default ThreadStatesConstructors implementation. Priority="+getPriority();
        }

        @Override
        public void close() throws IOException {
                System.out.println("Close of Enum");
        }
}

测试端:

import java.io.IOException;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.Set;

public class JavaEnumExamples {

        public static void main(String[] args) throws IOException {
                                
                usingEnumMethods();
                
                usingEnumValueOf();
                
                usingEnumValues();
                
                usingEnumInSwitch(ThreadStatesEnum.START);
                usingEnumInSwitch(ThreadStatesEnum.DEAD);
                
                usingEnumMap();
                
                usingEnumSet();
                
        }

        private static void usingEnumSet() {
                EnumSet<ThreadStatesEnum> enumSet = EnumSet.allOf(ThreadStatesEnum.class);
                for(ThreadStatesEnum tsenum : enumSet){
                        System.out.println("Using EnumSet, priority = "+tsenum.getPriority());
                }
        }

        private static void usingEnumMap() {
                EnumMap<ThreadStates, String> enumMap = new EnumMap<ThreadStates,String>(ThreadStates.class);
                enumMap.put(ThreadStates.START, "Thread is started");
                enumMap.put(ThreadStates.RUNNING, "Thread is running");
                enumMap.put(ThreadStates.WAITING, "Thread is waiting");
                enumMap.put(ThreadStates.DEAD, "Thread is dead");
                
                Set<ThreadStates> keySet = enumMap.keySet();
                for(ThreadStates key : keySet){
                        System.out.println("key="+key.toString()+":: value="+enumMap.get(key));
                }
                
        }

        private static void usingEnumInSwitch(ThreadStatesEnum th) {
                switch (th){
                case START:
                        System.out.println("START thread");
                        break;
                case WAITING:
                        System.out.println("WAITING thread");
                        break;
                case RUNNING:
                        System.out.println("RUNNING thread");
                        break;
                case DEAD:
                        System.out.println("DEAD thread");
                }
        }

        private static void usingEnumValues() {
                ThreadStatesEnum[] thArray = ThreadStatesEnum.values();
                
                for(ThreadStatesEnum th : thArray){
                        System.out.println(th.toString() + "::priority="+th.getPriority());
                }
        }

        private static void usingEnumValueOf() {
                ThreadStatesEnum th = Enum.valueOf(ThreadStatesEnum.class, "START");
                System.out.println("th priority="+th.getPriority());
        }

        private static void usingEnumMethods() throws IOException {
                ThreadStatesEnum thc = ThreadStatesEnum.DEAD;
                System.out.println("priority is:"+thc.getPriority());
                
                thc = ThreadStatesEnum.DEAD;
                System.out.println("Using overriden method."+thc.toString());
                
                thc = ThreadStatesEnum.START;
                System.out.println("Using overriden method."+thc.toString());
                thc.setPriority(10);
                System.out.println("Enum Constant variable changed priority value="+thc.getPriority());
                thc.close();
        }

}

运行结果:

priority is:4
Using overriden method.Default ThreadStatesConstructors implementation. Priority=4
Using overriden method.START implementation. Priority=1
Enum Constant variable changed priority value=10
Close of Enum
th priority=10
START implementation. Priority=10::priority=10
Default ThreadStatesConstructors implementation. Priority=2::priority=2
Default ThreadStatesConstructors implementation. Priority=3::priority=3
Default ThreadStatesConstructors implementation. Priority=4::priority=4
START thread
DEAD thread
key=START:: value=Thread is started
key=RUNNING:: value=Thread is running
key=WAITING:: value=Thread is waiting
key=DEAD:: value=Thread is dead
Using EnumSet, priority = 10
Using EnumSet, priority = 2
Using EnumSet, priority = 3
Using EnumSet, priority = 4