美文网首页
Java1.8-EnumSet源码分析

Java1.8-EnumSet源码分析

作者: 骑着乌龟去看海 | 来源:发表于2018-01-19 12:25 被阅读70次

    概述

      Set体系中有一个不常用的抽象类是EnumSet,这个类及对应的子类是专门为枚举服务的,所以EnumSet中的数据也都是枚举类型。

      EnumSet是一个抽象类,有两个子类:RegularEnumSet,JumboEnumSet,所以我们先分析这个抽象类,该类了解了,两个子类就很容易理解了。

    属性
    public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
        implements Cloneable, java.io.Serializable
    {
        // 保存的数据类型
        final Class<E> elementType;
        // 保存数据的数组
        final Enum<?>[] universe;
    }
    

    我们通过继承体系和官网文档可以看出:

    1. EnumSet是专门用于服务枚举类型的,内部存储是通过位向量来实现的,所以性能是很好的。
    2. EnumSet和其他Set不太一样的地方,是它是有序的并且不允许插入null。
    3. 不是线程同步的,如果要线程同步,可以使用Collections.synchronizedSet来同步;
    4. 所有基本操作都在常量时间内执行。它们很可能(虽然不能保证)比HashSet还要快很多,如果它们的参数也是枚举,即使是批量操作也会在常量时间内执行。
    5. 从JDK1.5开始引入。
    6. 方法基本上都是静态方法。

    方法

    构造方法
    EnumSet(Class<E>elementType, Enum<?>[] universe) {
        this.elementType = elementType;
        this.universe    = universe;
    }
    

    EnumSet只有一个构造方法,不过该方法不是public的,不对外使用,只供子类继承时使用。

    noneof方法

    因为EnumSet的其他大部分方法底层都是通过这个方法实现的,所以我们先看这个方法的源码:

    /**
     * 生成一个空的EnumSet,并指定其数据类型
     */
    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);
    }
    

    其中上面有一点很明显的就是:如果枚举元素的数量大于64,那么实际创建的是JumboEnumSet实现类,否则是RegularEnumSet实现类。

    allOf和of方法
    1. allOf方法是创建一个包含指定类型的所有元素的EnumSet,通过源码可以看到,是先调用noneof方法创建一个空的Set,然后在调用EnumSet的addAll方法;
    2. of方法有多个重载方法,参数从1到n,底层也是先调用noneof方法,再分n次调用EnumSet的add方法;
    copyOf方法
    /**
     * 创建一个EnumSet,从指定集合拷贝数据
     */
    public static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c) {
        // 判断如果集合类型是EnumSet类型,则调用clone方法拷贝
        if (c instanceof EnumSet) {
            return ((EnumSet<E>)c).clone();
        } else {
            // 如果集合是空,直接抛异常
            if (c.isEmpty())
                throw new IllegalArgumentException("Collection is empty");
            // 通过Iterator迭代器实现拷贝
            Iterator<E> i = c.iterator();
            E first = i.next();
            EnumSet<E> result = EnumSet.of(first);
            while (i.hasNext())
                result.add(i.next());
            return result;
        }
    }
    

    copyOf方法要求传入的集合参数不能是空集合,并且拷贝方式是通过迭代器的方式来实现的。其中还有一个重载的copyOf方法是调用该方法来实现的。

    complementOf方法
    public static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s) {
        EnumSet<E> result = copyOf(s);
        result.complement();
        return result;
    }
    

    这个方法不太好翻译,或许可以理解是一个过滤,或者说去除已有元素的方法。拿一个例子解释下:

    public static void main(String[] args) {
        EnumSet enumSet = EnumSet.of(Flag.RIGHT, Flag.ERROR);
        System.out.println(enumSet);
    
        EnumSet set = EnumSet.complementOf(enumSet);
        System.out.println(set);
    }
    

      上述enumSet中有枚举Flag的两个元素,通过complementOf方法之后,会得到枚举Flag中除了这两个元素之外的剩余元素。

    该方法底层调用的是complement方法,EnumSet中没有对该方法进行实现,具体的实现是相应的实现类来实现的。

    range方法
    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());
        result.addRange(from, to);
        return result;
    }
    

      前面已经提过,EnumSet是有序的,所以该方法是创建一个区间内的EnumSet,通过传入枚举类型的两个元素,获取该枚举类型里这两个元素之间的数据,同时也包含这两个元素。同样,底层的addRange方法也是抽象方法,由相应的实现类来实现。
      另外,EnumSet中还提供了一个静态内部类SerializationProxy来进行序列化工作。

    到这里,EnumSet的分析就结束了。

    参考文档:
    https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html

    相关文章

      网友评论

          本文标题:Java1.8-EnumSet源码分析

          本文链接:https://www.haomeiwen.com/subject/yfdooxtx.html