EnumSet源码分析

核心: 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,EnumSet就是用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;}
				
时间: 2024-10-24 22:42:44

EnumSet源码分析的相关文章

Alluxio源码分析:RPC框架浅析(一)

        Alluxio源码分析是一个基于内存的分布式文件系统,和HDFS.HBase等一样,也是由主从节点构成的.而节点之间的通信,一般都是采用的RPC通讯模型.Alluxio中RPC是基于何种技术如何实现的呢?它对于RPC请求是如何处理的?都涉及到哪些组件?本文将针对这些问题,为您一一解答.         一.Alluxio中RPC实现技术支持         Alluxio中的RPC是依靠Thrift实现的,Apache Thrift 是 Facebook 实现的一种高效的.支持多

Hhadoop-2.7.0中HDFS写文件源码分析(二):客户端实现(1)

一.综述       HDFS写文件是整个Hadoop中最为复杂的流程之一,它涉及到HDFS中NameNode.DataNode.DFSClient等众多角色的分工与合作.       首先上一段代码,客户端是如何写文件的: Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(conf); Path file = new Path("demo.txt"); FSDataOutputStream

WebWork2源码分析

web Author: zhuam   昨晚一口气看完了夏昕写的<<Webwork2_Guide>>,虽然文档资料很简洁,但仍不失为一本好的WebWork2书籍,看的出作者的经验和能力都是非常的老道,在此向作者的开源精神致敬,并在此引用夏昕的那句话So many open source projects, Why not Open your Documents?   今天下载了最新的WebWork2版本, 开始了源码分析,这份文档只能算是我的个人笔记,也没时间细细校对,且个人能力有

JUnir源码分析(一)

一.引子 JUnit源码是我仔细阅读过的第一个开源项目源码.阅读高手写的代码能学到一些好的编程风格和实现思路,这是提高自己编程水平行之有效的方法,因此早就想看看这些赫赫有名的框架是怎么回事了.今天就拿最简单的JUnit下手,也算开始自己的源码分析之路.   JUnit作为最著名的单元测试框架,由两位业界有名人士协力完成,已经经历了多次版本升级(了解JUnit基础.JUnit实践).JUnit总体来说短小而精悍,有不少值得我们借鉴的经验在里面:但是也有一些不足存在,当然这对于任何程序来说都是难免的

java io学习(三) 管道的简介,源码分析和示例

管道(PipedOutputStream和PipedInputStream)的简介,源码分析和示例 本章,我们对java 管道进行学习. java 管道介绍 在java中,PipedOutputStream和PipedInputStream分别是管道输出流和管道输入流. 它们的作用是让多线程可以通过管道进行线程间的通讯.在使用管道通信时,必须将PipedOutputStream和PipedInputStream配套使用. 使用管道通信时,大致的流程是:我们在线程A中向PipedOutputStr

java io学习(二)ByteArrayOutputStream的简介,源码分析和示例

ByteArrayOutputStream的简介,源码分析和示例(包括OutputStream) 前面学习ByteArrayInputStream,了解了"输入流".接下来,我们学习与ByteArrayInputStream相对应的输出流,即ByteArrayOutputStream. 本章,我们会先对ByteArrayOutputStream进行介绍,在了解了它的源码之后,再通过示例来掌握如何使用它. ByteArrayOutputStream 介绍 ByteArrayOutputS

java io学习(一)ByteArrayInputStream的简介,源码分析和示例

ByteArrayInputStream的简介,源码分析和示例(包括InputStream) 我们以ByteArrayInputStream,拉开对字节类型的"输入流"的学习序幕. 本章,我们会先对ByteArrayInputStream进行介绍,然后深入了解一下它的源码,最后通过示例来掌握它的用法. ByteArrayInputStream 介绍 ByteArrayInputStream 是字节数组输入流.它继承于InputStream. 它包含一个内部缓冲区,该缓冲区包含从流中读取

mahout源码分析之DistributedLanczosSolver(七) 总结篇

Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. 看svd算法官网上面使用的是亚马逊的云平台计算的,不过给出了svd算法的调用方式,当算出了eigenVectors后,应该怎么做呢?比如原始数据是600*60(600行,60列)的数据,计算得到的eigenVectors是24*60(其中的24是不大于rank的一个值),那么最后得到的结果应该是original_data乘以eigenVectors的转置这样就会得到一个600*24的矩阵,这样就达到了

mahout源码分析之DistributedLanczosSolver(五)

Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. 1. Job 篇 接上篇,分析到EigenVerificationJob的run方法: public int run(Path corpusInput, Path eigenInput, Path output, Path tempOut, double maxError, double minEigenValue, boolean inMemory, Configuration conf) thro