文章作者:Tyan
博客:noahsnail.com
1. EnumSet
EnumSet是Java Set接口的一个特别实现,在JDK 1.5中开始支持,Enum类型也正式引入到了Java中。与其它保存枚举常量的Set相比,EnumSet具有更好的性能,同时其也是Java中的优秀特性之一。下面从三个方面来介绍EnumSet,what,how,when。
2. What is EnumSet
EnumSet是Set接口的一个实现,它只能用来存储Enum常量或其子类,不能存储其它类型。EnumSet是设计模式中工厂方法创建实例的一个很好例子。
EnumSet被声明为abstract class类型,EnumSet有两种实现方式,RegularEnumSet和JumboEnumSet,但是这两种实现方式是包私有的,不能在包外访问,因此必须使用工厂方法来创建并返回EnumSet实例,不能通过构造函数来创建。EnumSet中提供了多种创建EnumSet实例的静态工厂方法,例如of方法(进行了函数重载),copyOf方法,noneOf方法等。
3. How EnumSet is implemented in Java
上面已经说了,EnumSet是一个抽象类,有两个具体实现:java.util.RegularEnumSet和java.util.JumboEnumSet。二者的主要区别在于前者使用long来表示元素的数量,而后者使用long[]来表示元素的数量。这二者中表示元素数量使用的是位域结构,即通过long的二进制位数来表示元素数量,例如:RegularEnumSet使用的是long表示元素数量,long数值是通过64位二进制表示的,因此其只能包含的元素最大数量为64,如果元素数目大于64,采用的是JumboEnumSet表示,JumboEnumSet的long[]中有一个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集合。由于是基于数组的访问,因此add,contains,next方法的时间复杂度为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
