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;
}


目录
相关文章
|
10月前
|
存储 Java 索引
ArrayList源码分析
ArrayList源码分析
41 1
|
存储 Java 容器
一文带你进行ArrayList 源码分析
一文带你进行ArrayList 源码分析
10876 1
|
存储 算法 Java
【Java集合框架 二】HashMap源码分析
【Java集合框架 二】HashMap源码分析
72 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与其他集合关键字的区别和优缺点 最后,进行一个总结
224 0
java集合系列(3)ArrayList(源码分析)
|
存储 Java API
java集合系列(2)Collection(源码分析)
前一篇博客,我们基本上认识了集合,从这篇博客开始参考API文档,和源码分析,详细的介绍每个集合类的使用,力求在源码的角度来分析,加深理解。
131 0
java集合系列(2)Collection(源码分析)
|
Java 索引
【集合框架】JDK1.8源码分析之ArrayList(六)
  分析了Map中主要的类之后,下面我们来分析Collection下面几种常见的类,如ArrayList、LinkedList、HashSet、TreeSet等。下面通过JDK源码来一起分析ArrayList底层是如何实现的。(PS:把JVM看完了之后终于可以有成片的时间来阅读源码了,感觉简直不能更爽)。
107 0
【集合框架】JDK1.8源码分析之ArrayList(六)
|
Java API
【集合框架】JDK1.8源码分析之LinkedHashMap(二)
  前面我们已经分析了HashMap的源码,已经知道了HashMap可以用在哪种场合,如果这样一种情形,我们需要按照元素插入的顺序来访问元素,此时,LinkedHashMap就派上用场了,它保存着元素插入的顺序,并且可以按照我们插入的顺序进行访问。
123 0
【集合框架】JDK1.8源码分析之LinkedHashMap(二)
|
存储
【集合框架】JDK1.8源码分析HashSet && LinkedHashSet(八)
  分析完了List的两个主要类之后,我们来分析Set接口下的类,HashSet和LinkedHashSet,其实,在分析完HashMap与LinkedHashMap之后,再来分析HashSet与LinkedHashSet,就会变成异常简单,下面开始进行分析。
96 0