• Java笔记(九)EnumMap & EnumSet


    EnumMap & EnumSet

    一、EnumMap

    一)基本用法

        public static Map<Size, Integer> countBySize(List<Clothes> clothes) {
            EnumMap<Size, Integer> map = new EnumMap<>(Size.class);
            for (Clothes c : clothes) {
                Size size = c.getSize();
                Integer count = map.get(size);
                if (count != null) {
                    map.put(size, count + 1);
                } else {
                    map.put(size, 1);
                }
            }
            return map;
        }
    
        public static void main(String[] args) {
            List<Clothes> clothes = Arrays.asList(new Clothes[]{
                    new Clothes("C001",Size.SMALL), new Clothes("C002", Size.LARGE),
                    new Clothes("C003", Size.LARGE), new Clothes("C004", Size.MEDIUM),
                    new Clothes("C005", Size.SMALL), new Clothes("C006", Size.SMALL),
            });
            System.out.println(countBySize(clothes)); //{SMALL=3, MEDIUM=1, LARGE=2}
        }

    EnumMap是保证顺序的:输出是按照键在枚举中的顺序。

    二)实现原理

    EnumMap的实例变量:

    private final Class<K> keyType; //类型信息
    private transient K[] keyUniverse; //表示键,是所有可能的枚举值
    private transient Object[] vals; //表示键对应的值
    private transient int size = 0; //表示键值对个数

    基本构造方法:

    public EnumMap(Class<K> keyType) {
        this.keyType = keyType;
        keyUniverse = getKeyUniverse(keyType); //最终调用了枚举类型的values方法,该方法返回所有可能的枚举值
        vals = new Object[keyUniverse.length];
    }

    保存键值对的put方法:

    public V put(K key, V value) {
        //检查键的类型
        typeCheck(key);
        //获取索引
        int index = key.ordinal();
        //放入值value
        Object oldValue = vals[index];
        //如果有值为null打包null值,这是为了区别null值与没有值
        vals[index] = maskNull(value);
        if(oldValue == null)
        size++;
        return unmaskNull(oldValue);
    }
        private static final Object NULL = new Object() {
            public int hashCode() {
                return 0;
            }
            public String toString() {
                return "java.util.EnumMap.NULL";
            }
        };
        private Object maskNull(Object value) {
            return (value == null ? NULL : value);
        }
        private V unmaskNull(Object value) {
            return(V) (value == NULL ? null : value);
        }

    二、EnumSet

    EnumSet的实现与EnumMap没有任何关系,而是用极为精简高效的位向量实现的。

    位向量是计算机程序中解决问题的一种常用方式,我们有必要理解和掌握。

    一)基本用法

    通过EnumSet的静态工厂方法,可以创建EnumSet对象,比如:

    //创建一个指定类型的EnumSet,不含任何元素,创建的EnumSet实际类型是EnumSet的子类
    public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType)

    用法举例:

    public enum Day {
        MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
    }
    EnumSet<Day> days = EnumSet.noneOf(Day.class);
    days.add(Day.FRIDAY);
    days.add(Day.SUNDAY);
    days.add(Day.MONDAY);
    System.out.println(days); //[MONDAY, FRIDAY, SUNDAY]

    EnumSet的其他静态工厂方法:

    //初始集合包括指定枚举类型的所有枚举值
    <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType)
    //初始集合包括枚举值中指定范围的元素
    <E extends Enum<E>> EnumSet<E> range(E from, E to)
    //包含指定集合的补集
    <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s)
    //包含参数中的所有元素
    <E extends Enum<E>> EnumSet<E> of(E e)
    <E extends Enum<E>> EnumSet<E> of(E e1, E e2)
    <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3)
    <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4)
    <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)
    <E extends Enum<E>> EnumSet<E> of(E first, E... rest)
    //包含参数中的所有元素
    <E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s)
    <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c)

     二、应用场景

    Worker[] workers = new Worker[]{
            new Worker("Tom", EnumSet.of(Day.FRIDAY, Day.MONDAY, Day.SATURDAY)),
            new Worker("Jerry", EnumSet.of(Day.MONDAY, Day.WEDNESDAY, Day.THURSDAY)),
            new Worker("Teddy", EnumSet.of(Day.TUESDAY, Day.FRIDAY, Day.WEDNESDAY))
    };
    //哪些天一个人都不会来
    EnumSet<Day> allDays = EnumSet.allOf(Day.class);
    for (Worker w : workers) {
        allDays.remove(w.getWorkingDays());
    }

    三、实现原理

    位向量:用一个位表示一个元素状态,用一组位表示集合状态,

    每个位对应一个元素,而状态只可能有两种。

    比如之前的枚举类Day,它有7个枚举值,一个Day的集合就可以用一个字节byte表示,

    最高位不用,设为0,从右到左,每位对应一个枚举值,1表示包含该元素,0表示不包含该元素。

    EnumSet的实现,首先是主要实例变量:

    final Class<E> elementType; //表示类型信息
    final Enum[] universe; //表示所有枚举值

    EnumSet自身没有记录元素个数的变量,也没有位向量,它们是子类维护的。

    对于RegularEnumSet,它用一个long类型表示位向量,代码为:

    private long elements = 0L;

    RegularEnumSet没有定义元素个数的变量,都是实时计算出来的:

    public int size() {
        return Long.bitCount(elements);
    }

    而对于JumboEnumSet,用一个long数组表示,有单独的size变量

    private long elements[];
    private int size = 0;

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

    RegularEnum 和 SetJumboEnumSet的构造方法:

    RegularEnumSet(Class<E>elementType, Enum[] universe) {
        super(elementType, universe);
    }
    JumboEnumSet(Class<E>elementType, Enum[] universe) {
        super(elementType, universe);
        elements = new long[(universe.length + 63) >>> 6];
    }
    EnumSet(Class<E>elementType, Enum[] universe) {
        this.elementType = elementType;
        this.universe = universe;
    }

    其他源码略。

    四、实现原理

    对于只有两种状态,且需要进行集合运算的数据,使用位向量表示

    、位运算进行处理,是计算机程序中一种常见的思维方式。

    Simple is important!
  • 相关阅读:
    Python之黏包
    Python的subprocess模块
    (经典)TCP粘包分析
    python--数据类型bytes
    python socket编程
    Python之模块和包
    Memcache的客户端连接系列(二) Python
    Memcache的客户端连接系列(一) Java
    Stunnel客户端安装和配置
    分布式数据库中间件的实现原理介绍四:平滑扩容
  • 原文地址:https://www.cnblogs.com/Shadowplay/p/10027271.html
Copyright © 2020-2023  润新知