java集合(三)Set集合之EnumSet详解

1.定义:

添加枚举类元素的专用集合类

2.与其他集合类区别:

EnumSet内部实现不使用常见的数据结构,比如数组(ArrayList),链表(LinkedList),哈系表(HashMap、Hashtable、HashSet),红黑树(TreeMap、TreeSet)而是使用位运算完成集合的基本操作

EnumSet是抽象类,只能通过静态工厂方法构造EnumSet对象,具体如下:

EnumSet noneOf(Class elementType):构造一个空的集合EnumSet allOf(Class elementType):构造一个包含枚举类中所有枚举项的集合EnumSet of(E e):构造包含1个元素的集合EnumSet of(E e1, E e2):构造包含2个元素的集合EnumSet of(E e1, E e2, E e3):构造包含3个元素的集合EnumSet of(E e1, E e2, E e3, E e4):构造包含4个元素的集合EnumSet of(E e1, E e2, E e3, E e4, E e5):构造包含5个元素的集合EnumSet of(E first, E... rest):构造包含多个元素的集合(使用可变参数)EnumSet copyOf(EnumSet s):构造包含参数中所有元素的集合EnumSet copyOf(Collection c):构造包含参数中所有元素的集合

3.EnumSet作为集合类基本操作方法实现原理(位运算):

说明:

从EnumSet的noneOf可以看出,当枚举类中的枚举项少于64时,返回的是RegularEnumSet类(EnumSet的实现类)对象,大于64,返回的是JumboEnumSet类对象,为了方便分析,后面同一使用RegularEnumSet类解释原理

// EnumSet#noneOf

public static > EnumSet noneOf(Class 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);

}

测试用的枚举类

enum Color{

RED("RED"),

BLUE("BLUE"),

YELLOW("YELLOW"),

BLACK("BLACK");

String name;

Color(String name){

this.name = name;

}

@Override

public String toString(){

return this.name;

}

}

3.1 add方法

public boolean add(E e) {

typeCheck(e);

long oldElements = elements;

elements |= (1L << ((Enum)e).ordinal());

return elements != oldElements;

}

ordinal()为每个枚举项的序号,从0开始,按声明顺序排列,如[RED,BLUE,YELLOW,BLACK]对应[0,1,2,3];1L << ((Enum)e).ordinal()(为了方便,这里称之为枚举值)则表示1*2^(e.ordinal()),Color.RED的ordianl()为0,对应枚举值为1*2^0=1,其实就是第ordinal()+1位(从右往左数,个位为第1位)为1其它位为0的十进制数,对应十进制数为00000001(这里假设是8bit,实际是long型32bit);则每个枚举项表示如下:

枚举项 序号 1L << ((Enum)e).ordinal() 枚举值

枚举项

序号

1L << ((Enum)e).ordinal()

枚举值

Color.RED

0

00000001

1

Color.BLUE

1

00000010

2

Color.YELLOW

2

00000100

4

Color.BLACK

3

00001000

8

elements |=就是对添加的不同元素的枚举值进行求和,相同元素相或时保持不变

3.2 remove方法

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;

}

按照之前的枚举值相加的想法,remove就是从总枚举值中减去待删除元素的枚举值,因为是位运算,没有直接相减,使用位操作elements &= ~(1L << ((Enum)e).ordinal());完成相减操作

3.3 contains方法

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;

}

contains方法就更好理解,每个枚举项的枚举值的值都不一样,且相互之间进行相与操作为0,使用总枚举值与要查询的枚举项的枚举值进行相与操作,如果为0,说明不存在该枚举项,否则存在