Java容器之JumboEnumSet源码分析
一、概述
JumboEnumSet
适用于枚举值个数大于64
个的枚举类,其底层实现跟RegularEnumSet
一样都是根据位是否为1
来判断该枚举值是否添加到了Set
中,不过因为枚举值个数大于64
个,无法用64
位的long
类型来记录所有的枚举值,所以将RegularEnumSet
中long
类型的elements
改成了一个long
类型数组,添加某个枚举值时,先将某个枚举值的ordinal
属性除以64
,算出该枚举值所属的elements
数组索引,再用ordinal
属性对64
求余,将对应的位标识位1
。
二、源码分析
2.1 构造方法
JumboEnumSet(Class<E>elementType, Enum<?>[] universe) {
super(elementType, universe);
//加上63的目的是为了对64整除后算出来的值能包含原来的
//以70为例,64整除的结果是1,1*64 小于原来的70,如果加上63,则整除的结果是2,大于原来的70
elements = new long[(universe.length + 63) >>> 6];
}
2.2 添加元素
2.2.1 add
public boolean add(E e) {
//检查e的枚举类型是否合法
typeCheck(e);
int eOrdinal = e.ordinal();
//左移6位就是除以64,算出该枚举所属的elements数组索引
int eWordNum = eOrdinal >>> 6;
//获取原来的值
long oldElements = elements[eWordNum];
//eOrdinal的值是大于64的,此处右移,实际移动的位数是eOrdinal对64求余的结果
//比如eOrdinal的值是68,1L<<68的结果就是1<<4,10000
elements[eWordNum] |= (1L << eOrdinal);
//判断原elements数组索引的元素是否发生改变,如果改变则说明添加的枚举值原来不存在
boolean result = (elements[eWordNum] != oldElements);
if (result) //为true说明添加了一个新元素,size加1
size++;
return result;
}
2.2.2 addAll
void addAll() {
//遍历所有的数组元素,将值置为-1,-1时所有位都是1
for (int i = 0; i < elements.length; i++)
elements[i] = -1;
//最后一个数组元素不是所有位都是1的,只有universe.length对64求余的结果对应的位置为1,所以需要右移将其他的位置为0
//以70为例,对64求余的结果就是6,-70的补码的后6位是111010,即58,64个1右移58后还剩6个1
elements[elements.length - 1] >>>= -universe.length;
size = universe.length;
}
2.2.3 addRange
void addRange(E from, E to) {
int fromIndex = from.ordinal() >>> 6;
int toIndex = to.ordinal() >>> 6;
//如果起始枚举值位于同一个数组元素中
if (fromIndex == toIndex) {
elements[fromIndex] = (-1L >>> (from.ordinal() - to.ordinal() - 1))
<< from.ordinal();
} else {
//起始枚举值不在同一个数组元素中
//此处是左移,将低于from的位都置为0
elements[fromIndex] = (-1L << from.ordinal());
//将fromIndex + 1到toIndex-1之间的数组元素都置为-1,所有位变成1
for (int i = fromIndex + 1; i < toIndex; i++)
elements[i] = -1;
//将to之前的低位都置为1
elements[toIndex] = -1L >>> (63 - to.ordinal());
}
//注意调用addRange时,JumboEnumSet是空的,所以此处直接赋值size
size = to.ordinal() - from.ordinal() + 1;
}
2.2.4 complement
void complement() {
//遍历所有数组元素,取非,原来为0的位变成1,原来为1的位变成0
for (int i = 0; i < elements.length; i++)
elements[i] = ~elements[i];
//处理最后一个数组元素,将多余的位都置为0
elements[elements.length - 1] &= (-1L >>> -universe.length);
//总长度减去原来的长度
size = universe.length - size;
}
2.3 移除元素
2.3.1 remove
public boolean remove(Object e) {
if (e == null)
return false;
Class<?> eClass = e.getClass();
if (eClass != elementType && eClass.getSuperclass() != elementType)
return false; //元素类型不符,返回false
//对64整除,算出所属的数组索引
int eOrdinal = ((Enum<?>)e).ordinal();
int eWordNum = eOrdinal >>> 6;
//将对应的数组元素的对应位置为0
long oldElements = elements[eWordNum];
elements[eWordNum] &= ~(1L << eOrdinal);
boolean result = (elements[eWordNum] != oldElements);
if (result) //如果改变说明原来是有这个枚举值的,size减1
size--;
return result;
}
2.3.2 removeAll
public boolean removeAll(Collection<?> c) {
if (!(c instanceof JumboEnumSet))
//调用父类removeAll,底层是遍历c,调用remove方法
return super.removeAll(c);
JumboEnumSet<?> es = (JumboEnumSet<?>)c;
if (es.elementType != elementType)
return false; //元素类型不符,返回false
//遍历当前Set,先对es.elements[i]求非,将原来为1的位变成0,再和elements[i]求且,
//实现将位于es.elements[i]中的元素都从elements[i]中移除了
for (int i = 0; i < elements.length; i++)
elements[i] &= ~es.elements[i];
//重新计算size,返回size是否改变
return recalculateSize();
}
//重新计算size,并返回size是否发生改变
private boolean recalculateSize() {
int oldSize = size;
size = 0;
for (long elt : elements)
//遍历elements,累加位为1的总数
size += Long.bitCount(elt);
//如果size发生改变,说明有删除元素了,返回true
return size != oldSize;
}
2.3.3 retainAll
public boolean retainAll(Collection<?> c) {
if (!(c instanceof JumboEnumSet))
//调用父类retainAll,底层是遍历当前Set,如果不在c中则将其移除
return super.retainAll(c);
JumboEnumSet<?> es = (JumboEnumSet<?>)c;
if (es.elementType != elementType) {
//元素类型不一致,将当前Set清空
boolean changed = (size != 0);
clear();
return changed;
}
//遍历当前Set,两者的数组元素求且,不在es.elements[i]中的位就会被置为0,实现元素移除的效果
for (int i = 0; i < elements.length; i++)
elements[i] &= es.elements[i];
//重新计算size,返回size是否改变
return recalculateSize();
}
2.3.4 clear
public void clear() {
//所有元素都置为0
Arrays.fill(elements, 0);
size = 0;
}
2.4 size
public int size() {
return size;
}
2.5 contains
public boolean contains(Object e) {
if (e == null)
return false;
Class<?> eClass = e.getClass();
if (eClass != elementType && eClass.getSuperclass() != elementType)
return false; //枚举类型不符,返回false
int eOrdinal = ((Enum<?>)e).ordinal();
//判断对应数组元素的对应位是否为0,如果是则说明不存在,返回false,否则返回true
return (elements[eOrdinal >>> 6] & (1L << eOrdinal)) != 0;
}
public boolean containsAll(Collection<?> c) {
if (!(c instanceof JumboEnumSet))
//调用父类containsAll,遍历c,如果有一个元素不在当前Set中,则返回false
return super.containsAll(c);
JumboEnumSet<?> es = (JumboEnumSet<?>)c;
if (es.elementType != elementType) //元素类型不符
return es.isEmpty();
for (int i = 0; i < elements.length; i++)
//elements[i]先求非,原来为1的位变成0,再求且,如果结果不为0,说明某个元素在
//elements[i]中不存在,在es.elements[i]中存在,即c中某个元素不在当前Set中,返回false
if ((es.elements[i] & ~elements[i]) != 0)
return false;
return true;
}
2.6 迭代器
public Iterator<E> iterator() {
return new EnumSetIterator<>();
}
private class EnumSetIterator<E extends Enum<E>> implements Iterator<E> {
/**
* 当前遍历的elements元素
*/
long unseen;
/**
* 当前遍历的elements数组索引
*/
int unseenIndex = 0;
/**
* 上一次返回的为1的位
*/
long lastReturned = 0;
/**
* 元素数组中最后返回的索引
*/
int lastReturnedIndex = 0;
EnumSetIterator() {
unseen = elements[0];
}
@Override
public boolean hasNext() {
//遍历找到下一个数组元素不为0的数组元素
while (unseen == 0 && unseenIndex < elements.length - 1)
unseen = elements[++unseenIndex];
return unseen != 0;
}
@Override
@SuppressWarnings("unchecked")
public E next() {
if (!hasNext())
throw new NoSuchElementException();
//逻辑同RegularEnumSet
lastReturned = unseen & -unseen;
lastReturnedIndex = unseenIndex;
unseen -= lastReturned;
return (E) universe[(lastReturnedIndex << 6)
+ Long.numberOfTrailingZeros(lastReturned)];
}
@Override
public void remove() {
if (lastReturned == 0)
throw new IllegalStateException();
final long oldElements = elements[lastReturnedIndex];
//对应的位置为0
elements[lastReturnedIndex] &= ~lastReturned;
if (oldElements != elements[lastReturnedIndex]) {
size--;
}
lastReturned = 0;
}
}