Java中的EnumSet_Effective Java 2.0_Item 1知识点

文章作者:Tyan
博客:noahsnail.com

1. EnumSet

EnumSet是Java Set接口的一个特别实现,在JDK 1.5中开始支持,Enum类型也正式引入到了Java中。与其它保存枚举常量的Set相比,EnumSet具有更好的性能,同时其也是Java中的优秀特性之一。下面从三个方面来介绍EnumSet,what,how,when。

2. What is EnumSet

EnumSetSet接口的一个实现,它只能用来存储Enum常量或其子类,不能存储其它类型。EnumSet是设计模式中工厂方法创建实例的一个很好例子。

EnumSet被声明为abstract class类型,EnumSet有两种实现方式,RegularEnumSetJumboEnumSet,但是这两种实现方式是包私有的,不能在包外访问,因此必须使用工厂方法来创建并返回EnumSet实例,不能通过构造函数来创建。EnumSet中提供了多种创建EnumSet实例的静态工厂方法,例如of方法(进行了函数重载),copyOf方法,noneOf方法等。

3. How EnumSet is implemented in Java

上面已经说了,EnumSet是一个抽象类,有两个具体实现:java.util.RegularEnumSetjava.util.JumboEnumSet。二者的主要区别在于前者使用long来表示元素的数量,而后者使用long[]来表示元素的数量。这二者中表示元素数量使用的是位域结构,即通过long的二进制位数来表示元素数量,例如:RegularEnumSet使用的是long表示元素数量,long数值是通过64位二进制表示的,因此其只能包含的元素最大数量为64,如果元素数目大于64,采用的是JumboEnumSet表示,JumboEnumSetlong[]中有一个long数值,就能表示64个元素,两个long数值就能表示128个元素,以此类推。

4. When to use EnumSet in Java

《Effective Java》中的Item 32讲述了一个EnumSet的使用场景,推荐去看一下。当你需要对枚举类型进行特定的分组时,你可以使用EnumSet。例如,一周中有七天,你想将周末单独分出来的时候(enum int pattern):

private enum DayOfWeek{
     MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
}

private EnumSet weekend = EnumSet.of(SATURDAY,SUNDAY);

5. Important points about EnumSet

  • 一个EnumSet中只能包含一种枚举类型。

  • EnumSet中不能放入null元素,放入会抛出空指针异常。

  • EnumSet是线程非安全的。

  • EnumSet的Iterator是自动防故障和弱一致的,不会抛出并发修改异常,即在迭代过程的中的修改结果不一定会在迭代过程中显示。

  • EnumSet是高性能的Java集合。由于是基于数组的访问,因此addcontainsnext方法的时间复杂度为O(1)。

  • 存储枚举常量时使用EnumSet而不要用HashSet

6. Source Code of EnumSet

EnumSet:

public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> implements Cloneable, java.io.Serializable {
    ...
    EnumSet(Class<E>elementType, Enum<?>[] universe) {
        this.elementType = elementType;
        this.universe    = universe;
    }

    /**
     * Creates an empty enum set with the specified element type.
     *
     * @param <E> The class of the elements in the set
     * @param elementType the class object of the element type for this enum
     *     set
     * @return An empty enum set of the specified type.
     * @throws NullPointerException if <tt>elementType</tt> is null
     */
    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:

class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
    ...
    /**
     * Bit vector representation of this set.  The 2^k bit indicates the
     * presence of universe[k] in this set.
     */
    private long elements = 0L;

    RegularEnumSet(Class<E>elementType, Enum<?>[] universe) {
        super(elementType, universe);
    }
    ...
}

JumboEnumSet:

class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> {
    private static final long serialVersionUID = 334349849919042784L;

    /**
     * Bit vector representation of this set.  The ith bit of the jth
     * element of this array represents the  presence of universe[64*j +i]
     * in this set.
     */
    private long elements[];

    // Redundant - maintained for performance
    private int size = 0;

    JumboEnumSet(Class<E>elementType, Enum<?>[] universe) {
        super(elementType, universe);
        elements = new long[(universe.length + 63) >>> 6];
    }
    ...
}

7. Example of EnumSet

import java.util.EnumSet;
import java.util.Set;

public class EnumSetDemo {

    private enum Color {
        RED(255, 0, 0), GREEN(0, 255, 0), BLUE(0, 0, 255);
        private int r;
        private int g;
        private int b;
        private Color(int r, int g, int b) {
            this.r = r;
            this.g = g;
            this.b = b;
        }
        public int getR() {
            return r;
        }
        public int getG() {
            return g;
        }
        public int getB() {
            return b;
        }
    }


    public static void main(String args[]) {
        // this will draw line in yellow color
        EnumSet<Color> yellow = EnumSet.of(Color.RED, Color.GREEN);
        drawLine(yellow);
        // RED + GREEN + BLUE = WHITE
        EnumSet<Color> white = EnumSet.of(Color.RED, Color.GREEN, Color.BLUE);
        drawLine(white);
        // RED + BLUE = PINK
        EnumSet<Color> pink = EnumSet.of(Color.RED, Color.BLUE);
        drawLine(pink);
    }


    public static void drawLine(Set<Color> colors) {
        System.out.println("Requested Colors to draw lines : " + colors);
        for (Color c : colors) {
            System.out.println("drawing line in color : " + c);
        }
    }
}

Output:
Requested Colors to draw lines : [RED, GREEN]
drawing line in color : RED
drawing line in color : GREEN

Requested Colors to draw lines : [RED, GREEN, BLUE]
drawing line in color : RED
drawing line in color : GREEN
drawing line in color : BLUE

Requested Colors to draw lines : [RED, BLUE]
drawing line in color : RED
drawing line in color : BLUE

参考资料:

1、https://jaxenter.com/enumset-in-java-regularenumset-vs-jumboenumset-106051.html

2、Effective Java 2.0版本中的Item 1,本文就是在看《Effective Java》时看到的EnumSet,才想要仔细研究一下EnumSet的。Item 32讲述了EnumSet的使用场景。

3、http://brokendreams.iteye.com/blog/2267485

4、http://javarevisited.blogspot.kr/2014/03/how-to-use-enumset-in-java-with-example.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值