书接上文,上一篇对 TreeMap 进行了介绍与分析,对于 Java 大部分经典数据结构都已介绍完毕,本篇将对 EnumSet 进行介绍与分析。
在阅读 EnumSet 源码的时候发现它的部分实现是在子类中完成,所以不上 EnumSet 的继承关系图。
/**
* 一个为了与枚举类型一起使用的特殊性的 {@link Set} 实现类。所有 enum set 的元素必须来自同一个指定的枚
* 举类型,显式的或者隐式的,在 set 被创建的时候。Enum sets 内部被当作 bit vectors 代表。这种表示法是
* 特别坚实和高效的。这个类的空间和时间的性能应该足够好来允许它当作传统的基于 int 的 “bit flags” 的一个
* 高质量的,类型安全的选项,如果例如 containsAll 和 retainAll 的参数也是一个 enum set,那么即使是扩
* 展操作也应该会运行的非常快。
*
* iterator 方法返回的迭代器以它们的自然顺序横穿元素s(enum 常数被定义的顺序)。返回的爹地爱情是弱一直
* 的:它永远不会抛出 {@link ConcurrentModificationException} 并可它可能或者不可能对于在迭代操作过
* 程中的修改操作显式任何影响。
*
* Null 元素s是不被允许的。试图插入一个 null 元素将抛出 {@link NullPointerException}。但是试图测试
* 或者移除一个 null 元素将,正常调用。
*
* 就像大部分 collection 的实现类s,EnumSet 不是线程安全的。如果多个线程并行访问了一个 enum set,它应
* 该从外部实现线程安全。这通常是通过在一些实现了线程安全的的对象中自然封装这个 enum set 来实现的。如果不
* 存在这样的对象,这个 set 应该使用 {@link Collections#synchronizedSet} 方法"包装"。这最好在创建
* 的时候完成,来防止意外的线程不安全的访问:
*
*
* Set s = Collections.synchronizedSet(EnumSet.noneOf(MyEnum.class));
*
*
* 实现类注意:所有继承操作都在一个常数时间内执行。它们看上去(虽然没有保证)比它们对应的 {@link
* HashSet} 对象更快。如果它们的参数也是一个 enum set,那么即使是扩展操作s执也将在常数时间内执行完。
*
* EnumSet 类继承自 AbstractSet 抽象类,实现了 Cloneable 接口与 Serializable 接口
*/
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, java.io.Serializable
{
/**
* 所有当前 set 的元素的类
*/
final Class<E> elementType;
/**
* 所有包含 T 的值。(为了性能缓存。)
*/
final Enum<?>[] universe;
private static Enum<?>[] ZERO_LENGTH_ENUM_ARRAY = new Enum<?>[0];
//带参构造器,一个元素类参数,一个枚举类型对象数组
EnumSet(Class<E>elementType, Enum<?>[] universe) {
//为两个类属性赋初值
this.elementType = elementType;
this.universe = universe;
}
/**
* 使用指定的元素类型创建一个空的 enum set。这个方法是 EnumSet 实现重要方法,虽然它本身是一个
* static 方法,但是每次调用都会返回一个新的对象。
*/
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
Enum<?>[] universe = getUniverse(elementType); //#getUniverse,获取枚举类型的所有实例
if (universe == null)
throw new ClassCastException(elementType + " not an enum");
if (universe.length <= 64) //如果 universe 的长度小于等于 64,这也是一种类似于 HashMap 中的决定 plain node 或者 tree node 的策略模式
return new RegularEnumSet<>(elementType, universe); //调用 RegularEnumSet 构造器
else
return new JumboEnumSet<>(elementType, universe); //如果 universe 的长度大于 64,调用 JumboEnumSet 构造器
}
/**
* 创建一个包含指定的元素类型的所有元素的 enum set。
*/
public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) {
EnumSet<E> result = noneOf(elementType); //#noneOf
result.addAll(); //调用 result 的 addAll 方法
return result;
}
/**
* 从适当的枚举类型中添加所有元素到这个 enum set 中,它调用调用之前是空的。这个方法必须由子类实现,
* 这里就可以看出 RegularEnumSet 与 JumboEnumSet 在 addAll 的实现上是不同的。
*/
abstract void addAll();
/**
* 创建一个与指定 enum set 拥有相同元素类型的 enum set,使用与它(指定 enum set) 相同的所有元素
* (如果有的话)初始化。
*/
public static <E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s) {
return s.clone(); //调用参数的 clone 方法
}
/**
* 从指定 collection 创建一个被初始化的 enum set。如果指定 collection 是一个 EnumSet 实例,这
* 个静态工厂方法与 {@link #copyOf(EnumSet)} 行为相同。否则的话,指定 collection 必须包含至少
* 一个元素(为了定义新 enum set 的元素类型)。
*/
public static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c) {
if (c instanceof EnumSet) { //如果参数是 EnumSet 实例
return ((EnumSet<E>)c).clone(); //调用 c.clone 方法
} else { //如果参数不是 EnumSet 实例
if (c.isEmpty())
throw new IllegalArgumentException("Collection is empty");
Iterator<E> i = c.iterator(); //获取参数的迭代器
E first = i.next(); //获取游标下一个元素(第一个元素)
EnumSet<E> result = EnumSet.of(first); //调用 EnumSet#of 缓存一个 EnumSet 对象
while (i.hasNext()) //循环如果游标之后还有与元素
result.add(i.next()); //步移加入 result
return result; //返回 result
}
}
/**
* 创建一个与指定 enum set 元素类型相同的 enum set,使用所有指定 set 中不包含类型的元素来初始化。
*/
public static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s) {
EnumSet<E> result = copyOf(s); //#copyOf
result.complement(); //调用 result.complement
return result; //返回 result
}
/**
* 创建一个被指定元素的初始化的 enum set。
*
* 存在这个方法的重载方法s使用一到五个元素来初始化一个 enum set。一个第六个参数的重载方法使用可变参
* 数功能实现。这个重载方法可以被用来建立一个包含任意数量元素的 enum set,但是看上去比不使用可变参数
* 的重载方法s更慢。单参版本
*/
public static <E extends Enum<E>> EnumSet<E> of(E e) {
EnumSet<E> result = noneOf(e.getDeclaringClass()); //#noneOf
result.add(e); //调用 result.add
return result; //返回 result
}
/**
* 双参版本
*/
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2) {
EnumSet<E> result = noneOf(e1.getDeclaringClass()); //#noneOf
result.add(e1); //调用两次 add
result.add(e2);
return result;
}
/**
* 三参版本
*/
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3) {
EnumSet<E> result = noneOf(e1.getDeclaringClass()); //#noneOf
result.add(e1); //调用三次 add
result.add(e2);
result.add(e3);
return result;
}
/**
* 四参版本
*/
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4) {
EnumSet<E> result = noneOf(e1.getDeclaringClass()); //#noneOf
result.add(e1); //调用四次 add
result.add(e2);
result.add(e3);
result.add(e4);
return result;
}
/**
* 五参版本
*/
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4,
E e5)
{
EnumSet<E> result = noneOf(e1.getDeclaringClass()); //#noneOf
result.add(e1); //调用五次 add
result.add(e2);
result.add(e3);
result.add(e4);
result.add(e5);
return result;
}
/**
* 变长版本
*/
@SafeVarargs
public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest) {
EnumSet<E> result = noneOf(first.getDeclaringClass()); //#noneOf
result.add(first);
for (E e : rest) //通过 for 循环调用 add
result.add(e);
return result;
}
/**
* 创建一个包含所有两个指定端点范围的所有元素的被初始化的 enum set。返回的 set 将包含两个端点,它们
* 可能相等但是必须不能超过。
*/
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()); //#noneOf
result.addRange(from, to); //调用 result.addRange 方法
return result; //返回 result
}
/**
* 向当前 enum set 中添加指定返回,它(enum set)在调用前是空的,同样需要子类实现。
*/
abstract void addRange(E from, E to);
/**
* 返回一份当前 set 的拷贝
*/
@SuppressWarnings("unchecked")
public EnumSet<E> clone() {
try {
return (EnumSet<E>) super.clone(); //调用 Object#clone
} catch(CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
/**
* 为当前 enum set 补充内容。
*/
abstract void complement();
/**
* 如果参数 e 不是当前 enum set 的正确类型,则抛出一个异常。
*/
final void typeCheck(E e) {
Class<?> eClass = e.getClass();
if (eClass != elementType && eClass.getSuperclass() != elementType)
throw new ClassCastException(eClass + " != " + elementType);
}
/**
* 返回所有包含 E 的值s。
* 返回值是不克隆的,缓存的,并且被所有调用者共享的。(嗯?线程安全问题?)
*/
private static <E extends Enum<E>> E[] getUniverse(Class<E> elementType) {
return SharedSecrets.getJavaLangAccess()
.getEnumConstantsShared(elementType);
}
/**
* 这个类被用来序列化所有 EnumSet 实例,不用考虑实现类类型。它捕获它们的 "逻辑内容" 并且它们被使用
* 公共静态工厂s重建。这是由必须要的来保证一个特定的实现类类型的存在是一个实现类的细节。
*
* @serial include
*
* SerializationProxy 类,实现了 Serializable 接口
*/
private static class SerializationProxy <E extends Enum<E>>
implements java.io.Serializable
{
/**
* 当前 enum set 的元素类型
*
* @serial
*/
private final Class<E> elementType;
/**
* 当前 enum set 中包含的元素
*
* @serial
*/
private final Enum<?>[] elements;
//构造器
SerializationProxy(EnumSet<E> set) {
elementType = set.elementType; //elementType 赋值为 set 的 elementType
elements = set.toArray(ZERO_LENGTH_ENUM_ARRAY); //一个空的数组,这个方法又是 ArrayList 中经常露面的 Arrays.copyOf 方法实现的
}
// 我们应该首选使用 elementType.cast() 而不是强转为 E,
// 为了避免伪造流注入,但是它将会减慢实现
@SuppressWarnings("unchecked")
private Object readResolve() {
EnumSet<E> result = EnumSet.noneOf(elementType); //EnumSet#noneOf
for (Enum<?> e : elements) //循环加入
result.add((E)e);
return result; //返回 result
}
private static final long serialVersionUID = 362491234563181265L;
}
//写替换
Object writeReplace() {
return new SerializationProxy<>(this); //返回 SerializationProxy 构造器对象
}
// 为了序列化代理模式提供的 readObject 方法
private void readObject(java.io.ObjectInputStream stream)
throws java.io.InvalidObjectException {
throw new java.io.InvalidObjectException("Proxy required");
}
}
通过 EnumSet 的源码可以看到它与 HashSet 与 TreeSet 不同,与 Map 没有任何关系,只是维护了一个枚举类型与该枚举类型数组,判断(获取)类型,添加与循环的方式也很简单,但是它会根据长度将选择具体的子类(RegularEnumSet 和 JumboEnumSet),也没有 Iterator 方法(猜测在子类中),addAll,addRange 与 complement 三个抽象方法都必须由子类提供实现。最后一个 readObject 方法可以看到 EnumSet 时间序列化读的方法交由代理类来实现。
在查看 RegularEnumSet 类之前先来看下 Enum 抽象类,因为这个类中某些方法的实现依赖于 Enum 抽象类。
/**
* 这是一个所有 Java 语言枚举类型的基类。
*
* 更多关于 enums 的信息,包括被编译器隐式的声明的方法s的描述,可以在 The Java™ Language
* Specification section 8.9 中找到。
*
* 注意当使用一个枚举类型作为一个 set 的类型或者作为一个 map 中键s的类型时,专门的和高效的 {@linkplain
* java.util.EnumSet set} 和 {@linkplain java.util.EnumMap map} 实现类是可以使用的。
*
* Enum 抽象类实现了 Comparable 接口与 Serializable 接口
*/
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
/**
* 这个 Enum 常数的名称,就和被定义在 enum 声明中的一样。大部分编程者s应该使用 {@link #toString} 方
* 法而不是直接访问这个类属性。
*/
private final String name;
/**
* 返回当前 enum 常数的名称,与在它的 enum 声明中声明的完全一样。
*
* 大部分编程者s应该使用 {@link #toString} 方法来使用而不是这个,就如 toString 方法可以返回一个更用
* 户友好的名称。这个方法主要被设计来在特定的正确性基于获取准确的名称情况下使用,不同版本之间不会有不同。
*/
public final String name() {
return name; //返回 name
}
/**
* 当前枚举常数的顺序(它在 enum 声明中的位置,初始常数被安排在 0 顺序)。
*
* 大部分编程者s将不会使用这个类属性。它是为复杂的基于枚举的数据结构而设计的,就像 {@link
* java.util.EnumSet} 和 {@link java.util.EnumMap}。
*/
private final int ordinal;
/**
* 返回当前迭代常数的顺序(它在它的 enum 声明中的位置,初始常数被安排在 0 顺序)。
*
* 大部分编程者s将不会使用这个方法。它是为复杂的基于枚举的数据结构而设计的,就像 {@link
* java.util.EnumSet} 和 {@link java.util.EnumMap}。
*/
public final int ordinal() {
return ordinal;
}
/**
* 唯一的构造器。编程者s不能调用这个构造器。它供编译器为响应枚举类型而发出的代码使用。
*/
protected Enum(String name, int ordinal) {
//名称初始化,顺序初始化
this.name = name;
this.ordinal = ordinal;
}
/**
* 返回当前 enum 常数的名称,如声明所载。这个方法可以被重写,尽管它通常不是必要的或者需要的。单个更“编程
* 者友好”string 形式存在时,一个 enum 类型应该重写这个方法。
*/
public String toString() {
return name; //不重写的话,返回 name
}
/**
* 如果指定的 object 和当前的 enum 常数 equal,返回 true。
*/
public final boolean equals(Object other) {
return this==other; //通过 == 比较
}
/**
* 返回当前 enum 常数的一个 hash 码。
*/
public final int hashCode() {
return super.hashCode(); //Object#hashCode
}
/**
* 抛出 CloneNotSupportedException。这保证了 enums 永远不会被克隆,这对于保护它们的"单例"状态是有
* 必要的。
*/
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
/**
* 比较当前 enum 和指定对象的序号。返回一个负整数,0,或者一个正数如果当前对象是小于,等于,或者大于指定
* 对象。
*
* Enum 常数只对另一个相同 enum 类型的 enum 常数可比较。这个方法实现的自然顺序是常数被声明的顺序。
*/
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // 优化
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal; //返回当前的序号减去参数的序号
}
/**
* 返回这个 Class 对象对应于这个 enum 常数的 enum 类型。当且仅当 e1.getDeclaringClass() ==
* e2.getDeclaringClass() 时,两个 enum 常数 e1 和 e2 是相同的 enum 类型。(对于具有常量特定类体
* s的 enum 常数这个方法返回的值可能与 {@link Object#getClass} 方法返回的不同。)
*/
@SuppressWarnings("unchecked")
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}
/**
* 返回带有指定名称的指定 enum 类型的 enum 常数。这个名称必须完全匹配一个这个类型的用来声明一个 enum
* 常数的识别符。(不允许使用外部空白字符。)
*
* 注意对于一个特别的 enum 类型 {@code T},在枚举上隐式地声明 {@code public static T
* valueOf(String)} 方法可以被使用而不是此方法将名称映射到响应的枚举常量。所有一个 enum 类型的常数s
* 可以通过调用那个类型的隐式的 {@code public static T[] values()} 方法被包含。
*/
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
String name) {
T result = enumType.enumConstantDirectory().get(name); //调用 Class#enumConstantDirectory 返回一个简单名称到枚举常数,然后调用 Map#get 方法
if (result != null) //如果 result 不为 null
return result; //返回 result
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}
/**
* enum 类似不能有 finalize 方法
*/
protected final void finalize() { }
/**
* 保护默认的反序列化
*/
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}
private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
}
由 Enum 抽象类的源码可以看到,其他许多方法是 Java 不建议编程者直接调用的,比如 name 属性,#name 方法,ordinal 属性,#ordinal 方法,构造器等等,这些属性和方法都是供编译器或者特定的 Java 数据结构类使用的。重要方法 valueOf 是调用 Class#enumConstantDirectory 将 enum 中的常数转换成一个 HashMap,再调用 HashMap#get 实现的。而它的 compareTo 方法之间比较的是两个对象的在 enum 类中的定义顺序。
RegularEnumSet 类中有无符号右移负数的操作,这里对于无符号右移有疑问的可以参考 Orcale Java 规范中对于位移运算的解释
If the promoted type of the left-hand operand is int, only the five lowest-order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator & with the mask value 0x1f (0b11111). The shift distance actually used is therefore always in the range 0 to 31, inclusive.
If the promoted type of the left-hand operand is long, then only the six lowest- order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator & with the mask value 0x3f (0b111111). The shift distance actually used is therefore always in the range 0 to 63, inclusive.
如果左操作数的提升类型是 int,只有右操作数的最低 5 位被用于转换。它就如同右操作数使用掩码值为 0x1f (0b11111) 来做逻辑位与。因此真正使用到的转换值在 0 到 31 之间,闭区间。
如果做操作数的提升类型是 long,那么只有操作数的最低 6 位被用于转换。它就如同右操作数使用掩码值为 0x3f (0b111111) 来做逻辑位与。因此真正使用到的转换值在 0 到 63 之间,闭区间。
还需要了解一下计算机中表示负数的方式(补码),以及关于补码的知识,这里引用 leonliu06 的一篇文章,这篇文章中做了很好的解释。
下面先来看下 RegularEnumSet 类
/**
* EnumSet 的私有实现类,对于"普通大小"的 enum 类型s(比如,那些 64 或者更小的 enum 常数s)。
*
* RegularEnumSet 类继承自 EnumSet 类
*/
class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
private static final long serialVersionUID = 3411599620347842686L;
/**
* 档表这个 set 的位向量。这个 2^k bit 表示当前 set 中 universe[k] 的存在。这个类属性的存在是因
* 为保存的时候并不是保存的实际元素,而是 bit,0 或者 1。它标志着被创建对象对于“单例”枚举需要包含哪
* 些元素。
*/
private long elements = 0L;
//单参构造器,一个元素类型参数,一个枚举类型元素数组
RegularEnumSet(Class<E>elementType, Enum<?>[] universe) {
super(elementType, universe); //调用 EnumSet 构造器
}
//重写 addRange 方法,与 noneOf 方法结合后,会创建一个新的 RegularEnumSet 对象,该对象中只包含 from 到 to 的元素
void addRange(E from, E to) {
/**
* -1L 的补码是 0xffffffffffffffff,无符号右移(开始位置 - 前往位置 - 1),只有低 6 位有
* 效,最高位补 0,假设 from.ordinal 为 1,to.orginal 为 5,那么括号中的数字就是 -5,-5 的
* 补码是 2 的 8 次方 - 5 即 251,0xfffffffb,取有效的低 6 为,那么实际的值时 0x3b,对应的
* 十进制是 59,也就是将 -1L 右移 59 位,得到的二进制进过正好是最后 5 为全部为 1,其余位全部为
* 0。推广到普遍情况,当 n 在 1~64 之间时,(-1L >>> -n)的结果是低 n 为全部为 1,高为全部为
* 0。正好覆盖了了整个 long 型的正范围。再左移 from.ordinal(在这个例子里是 1)位,所以最后的
* 结果是第 2 为到第 6 为全部为 1,其他位全部为 0。
**/
elements = (-1L >>> (from.ordinal() - to.ordinal() - 1)) << from.ordinal();
}
/**
* 重写 addAll 方法,与 noneOf 方法结合后,会创建一个新的 RegularEnumSet 对象,这个对象将包含这
* 个类型的枚举类中的所有元素。
**/
void addAll() {
if (universe.length != 0)
elements = -1L >>> -universe.length; //参考 addRange 方法
}
/**
* 重写 complement 方法,配合 complementOf 方法使用,complmentOf 会创建一个 RegularEnumSet
* 参数对象的拷贝,然后调用这个拷贝对象的 complement 方法。
**/
void complement() {
if (universe.length != 0) {
elements = ~elements;
elements &= -1L >>> -universe.length; //遮住未使用的 bits,返回的就是补集了
}
}
/**
* 返回一个涵盖当前 set 中所有元素的迭代器。这个迭代器以元素s的自然顺序横穿所有元素(就是 enum 常熟
* 被声明的顺序)。返回的迭代器是一个永远不会抛出 {@link ConcurrentModificationException}
* 的“快照”迭代器,当这个调用被调用后,元素按期存在的方式进行遍历。
*/
public Iterator<E> iterator() {
return new EnumSetIterator<>();
}
//EnumSetIterator 类,实现了 Iterator 接口
private class EnumSetIterator<E extends Enum<E>> implements Iterator<E> {
/**
* 一个代表元素在这个 set 中还没有被这个迭代器返回的元素s的位向量。
*/
long unseen;
/**
* 代表通过这个迭代器最后一个返回的,但没有被移除的元素,或者如果不存在这样的元素则是 0。
*/
long lastReturned = 0;
//空构造器
EnumSetIterator() {
unseen = elements; //unseen 赋值为 elements
}
//unseen 位向量之后还有值
public boolean hasNext() {
return unseen != 0; //判断 unseen 是否不为 0
}
//返回 unseen 位向量之后的值
@SuppressWarnings("unchecked")
public E next() {
if (unseen == 0)
throw new NoSuchElementException();
/**
* 这里依然假设 enum 类中有 5 个常数,那么它的 elements 就是 0x000000000000001f(最后
* 五位为 11111),此时迭代器调用 next 返回第一个元素, unseen 与 elements 相同,-
* unseen 就是 0xffffffffffffffe1(最后五位为 00001),与 unseen 位与后
* lastReturned 就是 0x0000000000000001(最后五位为 00001)
**/
lastReturned = unseen & -unseen;
/**
* 0x000000000000001e(11110),就是新的 unseen,下一次 next 时候 -unseen 就是
* 0xffffffffffffffe2(最后五位为 00010),与 unseen 位与后 lastReturned 就是
* 0x0000000000000002(最后五位为 00010)
**/
unseen -= lastReturned;
return (E) universe[Long.numberOfTrailingZeros(lastReturned)]; //通过 lastReturned 找到 Enum 数组对应位置的常数
}
//移除方法
public void remove() {
if (lastReturned == 0)
throw new IllegalStateException();
elements &= ~lastReturned; //lastReturned 按位去反再与 elements 逻辑与,则 elements 中 lastReturned 位置被置为 0
lastReturned = 0; //重置 lastReturned
}
}
/**
* 返回当前 set 中的元素的数量。
*/
public int size() {
return Long.bitCount(elements);
}
/**
* 如果当前 set 中不包含任何元素,返回 true。
*/
public boolean isEmpty() {
return elements == 0;
}
/**
* 如果当前 set 中包含指定元素,则返回 true。
*/
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; //1 左移顺序位,进行位与,这里直接判断的是序号对应的 elements 所在位置是否为存在标记
}
// 修改操作s
/**
* 如果指定元素不存在,向当前 set 中添加指它。
*/
public boolean add(E e) {
typeCheck(e);
long oldElements = elements;
elements |= (1L << ((Enum<?>)e).ordinal()); //1 左移参数序号,与 elements 位或
return elements != oldElements; //判断序号位置位或后是否与原来相同
}
/**
* 如果当前 set 中存在指定元素,则移除。
*/
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()); //1 左移参数序号,与 elements 位与
return elements != oldElements; //判断序号位置位与后是否与原来相同
}
// 扩展操作s
/**
* 如果当前 set 包含所有指定 collection 中的元素则返回 true。
*/
public boolean containsAll(Collection<?> c) {
if (!(c instanceof RegularEnumSet))
return super.containsAll(c);
RegularEnumSet<?> es = (RegularEnumSet<?>)c;
if (es.elementType != elementType)
return es.isEmpty();
return (es.elements & ~elements) == 0; //在 enum 类型相同的前提下,elements 按位取反后与参数的 elements 进行位与,在参数都包含的前提下位与不会出现标记为 1 的位,否则表示不是全部包含。
}
/**
* 向当前 set 中添加所有指定 collection 中的元素s。
*/
public boolean addAll(Collection<? extends E> c) {
if (!(c instanceof RegularEnumSet))
return super.addAll(c);
RegularEnumSet<?> es = (RegularEnumSet<?>)c;
if (es.elementType != elementType) {
if (es.isEmpty())
return false;
else
throw new ClassCastException(
es.elementType + " != " + elementType);
}
long oldElements = elements;
elements |= es.elements; //参数的 elements 与 elements 位或
return elements != oldElements; //如果不相同,说明参数的 elements 多余原本的 elements,添加成功
}
/**
* 从当前 set 中移除所有包含在指定 collection 中的元素s。
*/
public boolean removeAll(Collection<?> c) {
if (!(c instanceof RegularEnumSet))
return super.removeAll(c);
RegularEnumSet<?> es = (RegularEnumSet<?>)c;
if (es.elementType != elementType)
return false;
long oldElements = elements;
elements &= ~es.elements; //参数的 elements 按位取反与 element 位与
return elements != oldElements; //这样 elements 对应于 参数 elements 存在的位全部被置位 0 了
}
/**
* 保留只在当前 set 中存在的包含在指定 collection 中的元素s。
*/
public boolean retainAll(Collection<?> c) {
if (!(c instanceof RegularEnumSet))
return super.retainAll(c);
RegularEnumSet<?> es = (RegularEnumSet<?>)c;
if (es.elementType != elementType) {
boolean changed = (elements != 0);
elements = 0;
return changed;
}
long oldElements = elements;
elements &= es.elements; //elements 位与 参数的 elements,后赋值给 elements
return elements != oldElements; //判断 elements 是否与原来的相等
}
/**
* 从当前 set 中移除所有元素s。
*/
public void clear() {
elements = 0;
}
/**
* 比较指定对象与当前 set 的相等性。如果指定对象也是一个 set,两个 sets 中有相同的长度,并且给定
* set 中的成员都被包含在当前 set 中时,返回 true。
*/
public boolean equals(Object o) {
if (!(o instanceof RegularEnumSet))
return super.equals(o);
RegularEnumSet<?> es = (RegularEnumSet<?>)o;
if (es.elementType != elementType)
return elements == 0 && es.elements == 0;
return es.elements == elements; //通过 == 判断
}
}
通过 RegularEnumSet 的源码可以看到,RegularEnumSet 与其他的数据结构都不同,不同之处其他数据结构都与真实保存的数据类型无关,而它是针对于 Enum 对象,利用了它是“单例”的特性,但是 EnumSet 容器又是“原型”特性的。这两种特性体现在对于相同枚举类型的所有容器来说,它们对应位置的元素都是相同的,只不过不一定包含在某个容器中罢了。Java 使用位向量来以最高效的方式实现这种特性,每个容器只是维护了一个位向量,由于 Enum 中的元素一直在那,通过对于不同位的标志,来标记这个容器中包含了哪些元素,而添加,移除,判空,迭代等全部都是通过位运算实现的,不得不说这种实现方式真的很巧妙。至于迭代器的弱一致性,猜想可能是因为其 Enum 对象“单例”特性的缘故,所以在迭代器中也不会再去判断 expectedModCount 了。
然后再来看下 JumboEnumSet 的源码
/**
* EnumSet 的私有实现类,对于 “巨无霸” 枚举类型s(比如,那些元素数量大于 64 的)。
*
* JumboEnumSet 类,继承自 EnumSet 抽象类
*/
class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> {
private static final long serialVersionUID = 334349849919042784L;
/**
* 代表当前 set 的位向量。对于当前数组的 jth 元素的 ith 位代表了当前 set 中存在 universe[64 * j
* + i] 元素。
*/
private long elements[];
// 冗余 - 为了维护性能
private int size = 0;
//带参构造器,一个枚举类型参数,一个该类型元素数组参数
JumboEnumSet(Class<E>elementType, Enum<?>[] universe) {
super(elementType, universe); //调用 EnumSet 的构造器
elements = new long[(universe.length + 63) >>> 6]; //无符号右移 6 位即除以 64,简化了管理,也为之后的定位做准备
}
//重写 addRange 方法
void addRange(E from, E to) {
int fromIndex = from.ordinal() >>> 6; //from 的序号除以 64
int toIndex = to.ordinal() >>> 6; //to 的序号除以 64
if (fromIndex == toIndex) { //如果商相同
/**
* 这里与 RegularEnumSet 不同的地方是 RegularElementSet 将结果作为整个 elements,而
* JumboEnumSet 中只是作为 elements 数组中的一个数据,基本来 elements 可能类似
* 0x0000000000000002 这样,现在的 elements 是原来类型的一个数组
**/
elements[fromIndex] = (-1L >>> (from.ordinal() - to.ordinal() - 1))
<< from.ordinal();
} else { //如果商不同
elements[fromIndex] = (-1L << from.ordinal()); //将 -1 取补码 + 1 左移 from 的序号位,也就是说将 from 序号标志位置为 1,赋值给 elements fromIndex 位置
for (int i = fromIndex + 1; i < toIndex; i++) //对 elements 中的不同位置赋值
elements[i] = -1; //从 fromIndex 到 toIndex 的位置的数据都是 -1,意味着这些位置的起始到结束都是有对应 Enum 常数的
/**
* 由于这里是元素数量大于等于 64 的情况,所有无符号右移位右边必然小于 0,要先对这个小于 0
* 的数取补码加 1,然后取后六位(因为无符号右移左边的类型是 long)转 10 进制,将 -1 取补
* 码 + 1,左移这么多位,赋值给 elmenets toIndex 位置,也就是将 to 序号的标志位置为 1
**/
elements[toIndex] = -1L >>> (63 - to.ordinal());
}
size = to.ordinal() - from.ordinal() + 1; //长度为 to 的序号 - from 的序号 + 1
}
//重写 addAll 方法
void addAll() {
for (int i = 0; i < elements.length; i++) //循环从 0 到 elements 的长度
elements[i] = -1; //elements 中每个位置都置为 -1,以为这这些位置的起始到结束都是有对应 Enum 常数的
elements[elements.length - 1] >>>= -universe.length; //elements 最后位置的 long 型数据无符号右移负的 universe.length 补码 + 1 取后 6 位转 10 进制(0 ~ 63),右移这些位
size = universe.length; //universe 的长度赋值给 size
}
//重写 complement 方法
void complement() {
for (int i = 0; i < elements.length; i++) //循环 elements 的长度
elements[i] = ~elements[i]; //elements[i] 位置的 long 型数据按位取反后赋值给原来位置
elements[elements.length - 1] &= (-1L >>> -universe.length); // -1 取补码 + 1 无符号右移 -unverse.length 位,然后与 elements[elements.length - 1] 进行位与
size = universe.length - size; //size 赋值为 universe.length 与当前 size 的差
}
/**
* 返回一个涵盖当前 set 中所有元素的迭代器。这个迭代器以元素的自然顺序(就是这个 enum 常数被声明的顺
* 序)横穿它们。返回的迭代器是一个"弱一致的"迭代器将永远不会抛出 {@link
* ConcurrentModificationException}。
*/
public Iterator<E> iterator() {
return new EnumSetIterator<>();
}
//EnumSetIterator 类,实现了 Iterator 接口
private class EnumSetIterator<E extends Enum<E>> implements Iterator<E> {
/**
* 一个代表了在这个 set 的当前 "word" 中还没有被这个迭代器返回的一个位向量。
*/
long unseen;
/**
* unseen 对应的在 elements 数组中的位置。
*/
int unseenIndex = 0;
/**
* 代表了上一个当前迭代器返回的但还没有移除的元素,或者如果不存在这样的元素,则是 0。
*/
long lastReturned = 0;
/**
* lastReturned 对应的在 elements 数组中的位置。
*/
int lastReturnedIndex = 0;
//空构造器
EnumSetIterator() {
unseen = elements[0]; //将 unseen 赋值为 elements 数组中的第一个 long 型号数据(在 RegularEnumSet 中是直接赋值为完成的 elements)
}
//判断位向量之后是否还有元素
@Override
public boolean hasNext() {
while (unseen == 0 && unseenIndex < elements.length - 1) //循环如果 unseen 位 0 并且 unseenIndex 小于 elements 数组长度 - 1
unseen = elements[++unseenIndex]; //unseen 赋值为 element 数组当前位置的值
return unseen != 0; //返回判断 unseen 不为 0 的结果
}
//返回位向量之后的元素
@Override
@SuppressWarnings("unchecked")
public E next() {
if (!hasNext())
throw new NoSuchElementException();
lastReturned = unseen & -unseen; //这里与 RegularEnumSet 相同
lastReturnedIndex = unseenIndex; //将 unseenIndex 赋值给 lastReturnIndex
unseen -= lastReturned; //与 RegularEnumSet 相同
return (E) universe[(lastReturnedIndex << 6)
+ Long.numberOfTrailingZeros(lastReturned)]; //lastRetrnedIndex 乘以 64 + lastReturned 对应位置,返回的就是真实位置的数据
}
//移除方法
@Override
public void remove() {
if (lastReturned == 0)
throw new IllegalStateException();
final long oldElements = elements[lastReturnedIndex]; //找到 lastReturnedIndex 对应位置的 long 型数据
elements[lastReturnedIndex] &= ~lastReturned; //elements lastReturnIndex 位置的数据的 lastReturned 位置 0
if (oldElements != elements[lastReturnedIndex]) { //如果与原来的数据不相同
size--; //由于 lastReturned 针对一位,所以每次递减即可
}
lastReturned = 0; //重置 lastReturned
}
}
/**
* 返回当前 set 中 elements 元素的数量。
*/
public int size() {
return size; //返回 size
}
/**
* 如果当前 set 不包含任何元素,返回 true。
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 如果当前 set 包含指定元素,返回 true。
*/
public boolean contains(Object e) {
if (e == null)
return false;
Class<?> eClass = e.getClass();
if (eClass != elementType && eClass.getSuperclass() != elementType)
return false;
int eOrdinal = ((Enum<?>)e).ordinal();
/**
* 除以 64 找到对应 long 型数据,与左移序号位,由于 eOrdinal 肯定大于 64 为,所以这里是要先
* 去模再左移,这样比较出来就是真实的对应位置的位是否被标记为 1
**/
return (elements[eOrdinal >>> 6] & (1L << eOrdinal)) != 0;
}
// 修改操作
/**
* 如果指定元素还不存在,向当前 set 添加指定它。
*/
public boolean add(E e) {
typeCheck(e);
int eOrdinal = e.ordinal(); //先获取 e 的序号
int eWordNum = eOrdinal >>> 6; //除以 64,找到元素在 elements 数组中对应 long 型数据位置
long oldElements = elements[eWordNum]; //找到 elements 数组中对应位置的 long 型数据
elements[eWordNum] |= (1L << eOrdinal); //取模左移,与 elements 对应位置的 long 型数据位或,并将结果赋值给对应为自豪
boolean result = (elements[eWordNum] != oldElements); //判断前后是否相等
if (result) //如果不等
size++; //递增 size
return result; //返回 result
}
/**
* 如果指定元素存在,从当前的 set 中移除它。
*/
public boolean remove(Object e) {
if (e == null)
return false;
Class<?> eClass = e.getClass();
if (eClass != elementType && eClass.getSuperclass() != elementType)
return false;
int eOrdinal = ((Enum<?>)e).ordinal(); //取得 e 的序号
int eWordNum = eOrdinal >>> 6; //除以 64
long oldElements = elements[eWordNum]; //找到数组对应位置数据
elements[eWordNum] &= ~(1L << eOrdinal); //取模左移后按位取反,然后与对应位置数据位与,数据的对应位被置 0
boolean result = (elements[eWordNum] != oldElements); //比较前后
if (result) //如果不等
size--; //递减 size
return result; //返回 result
}
// 扩展操作
/**
* 如果当前 set 包含所有指定 collection 中的元素,则返回 true。
*/
public boolean containsAll(Collection<?> c) {
if (!(c instanceof JumboEnumSet))
return super.containsAll(c);
JumboEnumSet<?> es = (JumboEnumSet<?>)c;
if (es.elementType != elementType)
return es.isEmpty();
for (int i = 0; i < elements.length; i++) //循环 elements.length
if ((es.elements[i] & ~elements[i]) != 0) //如果参数 elements i 位置的 long 型数据与 elements i 位置的 long 行数据按位取反后位与结果位 0
return false; //都包含
return true; //参数存在更多的数据
}
/**
* 向当前 set 中添加所有指定 collection 中的元素。
*/
public boolean addAll(Collection<? extends E> c) {
if (!(c instanceof JumboEnumSet))
return super.addAll(c);
JumboEnumSet<?> es = (JumboEnumSet<?>)c;
if (es.elementType != elementType) {
if (es.isEmpty())
return false;
else
throw new ClassCastException(
es.elementType + " != " + elementType);
}
for (int i = 0; i < elements.length; i++) //循环 elements.length
elements[i] |= es.elements[i]; //位或并覆盖
return recalculateSize(); //#recalculateSize,由于是多位操作,无法直接判断新的长度
}
/**
* 从当前 set 中移除所有指定 collection 中包含的元素。
*/
public boolean removeAll(Collection<?> c) {
if (!(c instanceof JumboEnumSet))
return super.removeAll(c);
JumboEnumSet<?> es = (JumboEnumSet<?>)c;
if (es.elementType != elementType)
return false;
for (int i = 0; i < elements.length; i++) //循环 elements.length
elements[i] &= ~es.elements[i]; //按位取反,位与并覆盖
return recalculateSize(); //#recalculateSize
}
/**
* 在当前 set 中保留与指定 collection 中相同的所有元素。
*/
public boolean retainAll(Collection<?> c) {
if (!(c instanceof JumboEnumSet))
return super.retainAll(c);
JumboEnumSet<?> es = (JumboEnumSet<?>)c;
if (es.elementType != elementType) {
boolean changed = (size != 0);
clear();
return changed;
}
for (int i = 0; i < elements.length; i++) //循环 elements.length
elements[i] &= es.elements[i]; //位与并覆盖
return recalculateSize(); //#recalculateSize
}
/**
* 移除当前 set 中的所有元素
*/
public void clear() {
Arrays.fill(elements, 0); //使用 Arrays 工具方法将 0 添入 elements 长度的数组中,即当前容器中不包含任何元素
size = 0; //重置 size
}
/**
* 比较指定对象与当前 set 的相等性。如果给定对象也是一个 set,两个 sets 有相同的长度,并且每个给定 set 中的元素都包含在当前 set 中,则返回 true。
*/
public boolean equals(Object o) {
if (!(o instanceof JumboEnumSet))
return super.equals(o);
JumboEnumSet<?> es = (JumboEnumSet<?>)o;
if (es.elementType != elementType)
return size == 0 && es.size == 0;
return Arrays.equals(es.elements, elements); //使用 Arrays.equals 方法判断参数数组与 elements 是否相同
}
/**
* 重写计算这个 set 的长度。如果它被修改了,则返回 true。
*/
private boolean recalculateSize() {
int oldSize = size; //缓存就长度
size = 0;
for (long elt : elements) //循环 elements 数组
size += Long.bitCount(elt); //或者每个 long 型数据对应的元素个数,累加 size
return size != oldSize; //比较当前 size 是否等于之前的,返回结果
}
//克隆方法
public EnumSet<E> clone() {
JumboEnumSet<E> result = (JumboEnumSet<E>) super.clone(); //Object#clone
result.elements = result.elements.clone(); //重新复制一份数组给 result.elements
return result; //返回 result
}
}
通过 JumboEnumSet 的源码可以看到,它与 RegularEnumSet 的逻辑是基本相同的,对于元素超过 63 的情况,它将所有元素以 64 为长度分割为一个数组,然后通过核心代码 >>> 6,<< 6,与核心属性来找到元素真实的位置,同时因为 JumboEnumSet 中的元素根据自然顺序(声明顺序)排序,所以是连续的,所以可以更高效的赋值。而数组的特性又使得一些方法的实现可以使用 Arrays 工具类来完成。再次感叹位操作的设计之美。。
以上就是对于 Set 的最终实现类 EnumSet 的介绍与分析,下一篇将对 EnumMap 进行介绍与分析。