EnumSet源码分析

简介: <div style="font-family:微软雅黑; font-size:14px; line-height:21px; widows:auto"><span style="background-color:inherit">核心: <span style="color:#ff0000">long(long数组) 和 位运算</span>,</span></div> <div st
核心: long(long数组) 和 位运算
            其存储结构elements并未直接存枚举本身,而是位标识,枚举存于elementType中的enumConstants中.

1、内部元素为枚举;
2、内部表示为 位向量,使用“位标志”的替换形式。(类似0x1000000,按bit存储,用位运算进行相关操作);
3、全部是静态方法static;
4、根据传入的枚举类型判断组成长度,小于等于64则返回RegularEnumSet,否则JumboEnumSet。
/**
* Creates an empty enum set with the specified element type.
*/
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

5、add源码
/**
* Adds the specified element to this set if it is not already present.
* @throws NullPointerException if <tt>e</tt> is null
*/
public boolean add(E e) {
typeCheck(e); //
long oldElements = elements;
elements |= (1L << ((Enum<?>)e).ordinal());
return elements != oldElements;
}
每个枚举值都对应着自己的ordinal,第一个枚举值的ordinal为0,第二个为1,以此类推。
看看RegularEnumSet中elements的定义吧。关键点:位向量、2^k bit则说明存在该元素(每位都为1)。
/**
* Bit vector representation of this set. The 2^k bit indicates the
* presence of universe[k] in this set.
*/
private long elements = 0L;

1、取枚举值的ordinal,初始为0;
2、1 << ordinal ;
3、与elements做 |= 运算。
例:
a.添加ordinal为0的枚举值,则计算后elements为1
b.添加ordinal为1的枚举值,则计算后elements为( 01|10 ) = 11(B)
c.添加ordinal为2的枚举值,则计算后elements为(011 | 100) = 111
    So,EnumS et就是用long型来存储枚举,支持64位(long64位)。

而ordinal是个什么鬼呢?
    是Enum抽象类的一个属性,源码如下:
private final int ordinal;
/**
* Returns the ordinal of this enumeration constant (its position
* in its enum declaration, where the initial constant is assigned
* an ordinal of zero).
*/
public final int ordinal() {
return ordinal;
}
注意看:这里被调用的是ordinal()方法,以便于后续版本更新时,保持良好的兼容性(即使计算方式改变了,也不会影响原来的工程)。

6、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;
}
1、1L << ((Enum)e).ordinal() ;
2、与elements做&运算。
例:
    ordinal为2,则elements为111(B);
1L << ((Enum<?>)e).ordinal() 值为 100(B);
elements & (1L << ((Enum<?>)e).ordinal()) = 111(B)
111不等于0,说明包含该元素。

利用 long 和 位运算 实现EnumSet的快速存储和判断。

7、size源码
public int size() {
return Long.bitCount(elements);
}
bitCount返回的是参数二进制码中1的个数。如110(B)返回2。
源码如下,太过高深,在此就不剖析了。
 public static int  bitCount(long i)
    {
        i -= i >>> 1 & 6148914691236517205L;
        i = (i & 3689348814741910323L) + (i >>> 2 & 3689348814741910323L);
        i = i + (i >>> 4) & 1085102592571150095L;
        i += i >>> 8;
        i += i >>> 16;
        i += i >>> 32;
        return (int)i & 127;
    }



JumboEnumSet
/**
* 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[];
由RegularEnumSet中的long型转为long型数组。

public boolean add(E e) {
typeCheck(e);
int eOrdinal = e.ordinal();
int eWordNum = eOrdinal >>> 6; // 无符号右移,高位补0,去掉低位
// eWordNum 表示 eOrdinal 的高位
    // 左移乘2,右移除2,移位位数对32取模( i>>33 等价于 i>>1)
long oldElements = elements[eWordNum];
elements[eWordNum] |= (1L << eOrdinal); // 核心,Enum前64个元素全部放在elements[0]中
    // elements[0]的值的二进制 第i位为1,则表示Enum的第i个元素存在于该集合,
    // 引申[64*j +i],elements[j]的第i个元素为1。
boolean result = (elements[eWordNum] != oldElements);
if (result)
size++;
return result;
}
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();
return (elements[eOrdinal >>> 6] & (1L << eOrdinal)) != 0;
}
 
 
NOte:正数一直无符号右移,将变为0。
例:
"eOrdinal"    65    
"1L << eOrdinal"    2    
"eOrdinal >>> 6"    1      // 待查元素的高位
"elements[eOrdinal >>> 6]"    35     // elements第j号位所有元素
" elements[eOrdinal >>> 6] & (1L << eOrdinal) "    2    

// Redundant - maintained for performance
private int size = 0;
public int size() {
return size;
}


目录
相关文章
|
存储 Java 索引
ArrayList源码分析
ArrayList源码分析
|
存储 Java 容器
一文带你进行ArrayList 源码分析
一文带你进行ArrayList 源码分析
10894 1
|
存储 算法 Java
【Java集合框架 二】HashMap源码分析
【Java集合框架 二】HashMap源码分析
89 0
Java集合源码剖析——基于JDK1.8中HashSet、LinkedHashSet的实现原理
Java集合源码剖析——基于JDK1.8中HashSet、LinkedHashSet的实现原理
Java集合源码剖析——基于JDK1.8中HashSet、LinkedHashSet的实现原理
|
存储 安全 Java
java集合系列(3)ArrayList(源码分析)
这篇文章开始介绍ArrayList。ArrayList基本上是我们在平时的开发当中,使用最多的一个集合类了,它是一个其容量能够动态增长的动态数组。所以这篇文章,旨在从源码的角度进行分析和理解。为了使得文章更加有条理,还是先给出这篇文章的大致脉络: 首先,ArrayList的基本介绍和源码API(只给出方法分析,重要的方法给出详细代码)。 然后,介绍遍历ArrayList的几种方式 接下来,叙述一下ArrayList与其他集合关键字的区别和优缺点 最后,进行一个总结
244 0
java集合系列(3)ArrayList(源码分析)
|
存储 Java API
java集合系列(2)Collection(源码分析)
前一篇博客,我们基本上认识了集合,从这篇博客开始参考API文档,和源码分析,详细的介绍每个集合类的使用,力求在源码的角度来分析,加深理解。
164 0
java集合系列(2)Collection(源码分析)
|
Java
Java集合源码分析之超级接口:Collection
Collection Collection是List、Queue和Set的超集,它直接继承于Iterable,也就是所有的Collection集合类都支持for-each循环。除此之外,Collection也是面向接口编程的典范,通过它可以在多种实现类间转换,这也是面向对象编程的魅力之一。 方法定义 在阅读源码前,我们可以先自行想象一下,如果我们想封装下数组或链表以方便操作,我们需要封装哪些功能呢?比如:统计大小、插入或删除数据、清空、是否包含某条数据,等等。而Collection就是对这些常用操作进行提取,只是其很全面、很通用。下面我们看看它都提供了哪些方法。
222 0
EnumSet源码解析
EnumSet源码解析
627 0
|
安全 Java 存储
java集合之ConcurrentSkipListSet源码分析——Set大汇总
java集合之ConcurrentSkipListSet源码分析——Set大汇总问题(1)ConcurrentSkipListSet的底层是ConcurrentSkipListMap吗? (2)ConcurrentSkipListSet是线程安全的吗? (3)ConcurrentSkipListSet是有序的吗? (4)ConcurrentSkipListSet和之前讲的Set有何不同? 简介ConcurrentSkipListSet底层是通过ConcurrentNavigableMap来实现的,它是一个有序的线程安全的集合。
1366 0