文章作者: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