概述
Set体系中有一个不常用的抽象类是EnumSet,这个类及对应的子类是专门为枚举服务的,所以EnumSet中的数据也都是枚举类型。
EnumSet是一个抽象类,有两个子类:RegularEnumSet,JumboEnumSet,所以我们先分析这个抽象类,该类了解了,两个子类就很容易理解了。
属性
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, java.io.Serializable
{
// 保存的数据类型
final Class<E> elementType;
// 保存数据的数组
final Enum<?>[] universe;
}
我们通过继承体系和官网文档可以看出:
- EnumSet是专门用于服务枚举类型的,内部存储是通过位向量来实现的,所以性能是很好的。
- EnumSet和其他Set不太一样的地方,是它是有序的并且不允许插入null。
- 不是线程同步的,如果要线程同步,可以使用Collections.synchronizedSet来同步;
- 所有基本操作都在常量时间内执行。它们很可能(虽然不能保证)比HashSet还要快很多,如果它们的参数也是枚举,即使是批量操作也会在常量时间内执行。
- 从JDK1.5开始引入。
- 方法基本上都是静态方法。
方法
构造方法
EnumSet(Class<E>elementType, Enum<?>[] universe) {
this.elementType = elementType;
this.universe = universe;
}
EnumSet只有一个构造方法,不过该方法不是public的,不对外使用,只供子类继承时使用。
noneof方法
因为EnumSet的其他大部分方法底层都是通过这个方法实现的,所以我们先看这个方法的源码:
/**
* 生成一个空的EnumSet,并指定其数据类型
*/
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);
}
其中上面有一点很明显的就是:如果枚举元素的数量大于64,那么实际创建的是JumboEnumSet实现类,否则是RegularEnumSet实现类。
allOf和of方法
- allOf方法是创建一个包含指定类型的所有元素的EnumSet,通过源码可以看到,是先调用noneof方法创建一个空的Set,然后在调用EnumSet的addAll方法;
- of方法有多个重载方法,参数从1到n,底层也是先调用noneof方法,再分n次调用EnumSet的add方法;
copyOf方法
/**
* 创建一个EnumSet,从指定集合拷贝数据
*/
public static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c) {
// 判断如果集合类型是EnumSet类型,则调用clone方法拷贝
if (c instanceof EnumSet) {
return ((EnumSet<E>)c).clone();
} else {
// 如果集合是空,直接抛异常
if (c.isEmpty())
throw new IllegalArgumentException("Collection is empty");
// 通过Iterator迭代器实现拷贝
Iterator<E> i = c.iterator();
E first = i.next();
EnumSet<E> result = EnumSet.of(first);
while (i.hasNext())
result.add(i.next());
return result;
}
}
copyOf方法要求传入的集合参数不能是空集合,并且拷贝方式是通过迭代器的方式来实现的。其中还有一个重载的copyOf方法是调用该方法来实现的。
complementOf方法
public static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s) {
EnumSet<E> result = copyOf(s);
result.complement();
return result;
}
这个方法不太好翻译,或许可以理解是一个过滤,或者说去除已有元素的方法。拿一个例子解释下:
public static void main(String[] args) {
EnumSet enumSet = EnumSet.of(Flag.RIGHT, Flag.ERROR);
System.out.println(enumSet);
EnumSet set = EnumSet.complementOf(enumSet);
System.out.println(set);
}
上述enumSet中有枚举Flag的两个元素,通过complementOf方法之后,会得到枚举Flag中除了这两个元素之外的剩余元素。
该方法底层调用的是complement方法,EnumSet中没有对该方法进行实现,具体的实现是相应的实现类来实现的。
range方法
public static <E extends Enum<E>> EnumSet<E> range(E from, E to) {
if (from.compareTo(to) > 0)
throw new IllegalArgumentException(from + " > " + to);
EnumSet<E> result = noneOf(from.getDeclaringClass());
result.addRange(from, to);
return result;
}
前面已经提过,EnumSet是有序的,所以该方法是创建一个区间内的EnumSet,通过传入枚举类型的两个元素,获取该枚举类型里这两个元素之间的数据,同时也包含这两个元素。同样,底层的addRange方法也是抽象方法,由相应的实现类来实现。
另外,EnumSet中还提供了一个静态内部类SerializationProxy来进行序列化工作。
到这里,EnumSet的分析就结束了。
参考文档:
https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html
网友评论