枚举

枚举

《Java编程的逻辑》读书笔记

1. 基础

定义:

public enum Size {
    SAMLL, MEDIUM, LARGE
}

简单使用:

    public static void main(String[] args) {
        Size size = Size.SAMLL;
        System.out.println(size.toString()); // SAMLL
        System.out.println(size.name()); // SAMLL
        System.out.println(size == Size.SAMLL); // true
        System.out.println(size.equals(Size.MEDIUM)); // false
        System.out.println(size.ordinal()); // 0
        
        for (Size size : Size.values()) {
            System.out.println(size);
        }
    }
/*
for输出:
SAMLL
MEDIUM
LARGE
*/

也可以作为switch-case的参数使用

枚举类内部有 int ordinal() 方法,返回枚举声明时的顺序,从0开始,声明顺序改变同一个枚举返回的值也会改变

2. 进阶

package com.wang.controller;

public enum WzkEnum {
    KUANG("狂","kuang"),
    ZHUAI("拽","zhuai"),
    KU("酷","ku"),
    SHUAI("帅","shuai"),
    DIAO("屌","diao"),
    ZHA("炸","zha"),
    TIAN("天","tian");
    private String chinese;
    private String pinyin;

    public String getChinese() {
        return chinese;
    }

    public void setChinese(String chinese) {
        this.chinese = chinese;
    }

    public String getPinyin() {
        return pinyin;
    }

    public void setPinyin(String pinyin) {
        this.pinyin = pinyin;
    }

    WzkEnum(String chinese, String pinyin) {
        this.chinese = chinese;
        this.pinyin = pinyin;
    }
}

枚举也可以这样使用,定义一个构造方法。

枚举,只写内容列队后面不用分号,如果带入参数,要在内容的列队后面写上分号

优点

  1. 语法更为简洁,之前要定义静态变量来实现类似功能
  2. 更安全,一个枚举的值要么为null要么为枚举值之一,不会有其他值
  3. 自带便利方法:values,valueOf,toString等

EnumMap

这里不详细介绍了,使用上跟其他map相同,只是构造方法需要传入一个枚举类。

内部:

实例变量:

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

超简单:两个数组,长度相同,一个表示可能的键(枚举值),一个表示对应的值,值为null表示没有该键值对。
根据索引可以直接访问键和值,效率很高,如果需要一个Map且键是枚举类型,那么应该使用EnumMap。

EnumSet

EnumSet可以非常高效的实现Set接口。
HashSet/TreeSet,内部是使用对应的HashMap/TreeMap实现的,但EnumSet不是,它的实现和EnumMap没有半毛钱关系,它是用极为精简和高效的 位向量 实现的。

它是处理枚举类型数据的一把利器,在一些作用领域,它非常高效方便。

基本用法

EnumSet是一个抽象类 它不能通过直接new创建
它提供了若干静态工厂方法,可以创建EnumSet对象:

// 创建一个指定枚举类型的EnumSet,不含任何元素,实际上创建的对象是EnumSet的子类
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType)
// 初始集合包含指定枚举类型的所有枚举值
public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) 
// 初始集合包含枚举值中指定范围的元素
public static <E extends Enum<E>> EnumSet<E> range(E from, E to)
// 包括指定集合的补集
public static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s)
// 初始集合包含参数中的所有元素---之所以这么多固定个数的方法是因为可变参数的运行效率低一些
public static <E extends Enum<E>> EnumSet<E> of(E e)
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2)
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3)
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4)
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)
public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest)
// 包括参数容器中的所有元素
public static <E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s)
public static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c)

应用场景

在一些工作中,比如客服、医生,并不是每个人每天都在,每个人工作的时间是不一样的。朱元璋可能周一、三、五上班,王保保可能周一二四六上班,给定每个人的工作时间,我们可能面对一些问题:

  1. 有没有哪天都不上班
  2. 有哪天至少会有一人上班
  3. 有哪天至少有两人上班
  4. 哪天人都来,以便开会
  5. 哪些人周一、二都会来

使用EnumSet可以方便高效的回答这些问题
我们定义一个枚举类week

public enum Weeks {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

再定义一个表示工作人员的类Worker:


public class Worker {

    String name;
    Set<Weeks> availableDays;

    public Worker(String name, Set<Weeks> availableDays) {
        this.name = name;
        this.availableDays = availableDays;
    }
    // 省略getter方法
}

定义一个工作人员数组:

	//    工作人员数组
    static Worker[] workers = new Worker[]{
            // 老朱 一二三五上班
            new Worker("朱元璋", EnumSet.of(MONDAY, TUESDAY, WEDNESDAY, FRIDAY)),
            // 小朱 二四六上班
            new Worker("朱标", EnumSet.of(TUESDAY, THURSDAY, SATURDAY)),
            // 小小朱二四上班
            new Worker("朱允炆", EnumSet.of(TUESDAY, THURSDAY)),
            };

  1. 哪天都不上班:
        Set<Weeks> days = EnumSet.allOf(Weeks.class);
        for (Worker worker : workers) {
            days.removeAll(worker.getAvailableDays());
        }
        System.out.println(days); // [SUNDAY]
  1. 哪天至少会有一人上班
        // 2. 哪天至少会有一人上班(并集)
        Set<Weeks> days2 = EnumSet.noneOf(Weeks.class);
        for (Worker worker : workers) {
            days2.addAll(worker.getAvailableDays());
        }
        System.out.println(days2); // [MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY]
  1. 哪天所有人都来
        // 3. 哪天都会来(交集)
        Set<Weeks> days3 = EnumSet.allOf(Weeks.class);
        for (Worker worker : workers) {
            days3.retainAll(worker.getAvailableDays());
        }
        System.out.println(days3); // [TUESDAY]
  1. 哪些人周一 周二会来
        // 4. 哪些人周一 周二会来
        Set<Worker> availableWorkers = new HashSet<>();
        for (Worker worker : workers) {
            if (worker.getAvailableDays().containsAll(EnumSet.of(Weeks.MONDAY,Weeks.TUESDAY))){
                availableWorkers.add(worker);
            }
        }
        for (Worker availableWorker : availableWorkers) {
            System.out.println(availableWorker.getName());
        }
        // 朱元璋
  1. 哪些天至少有两个人来
// 5. 哪些天至少有两个人来
        EnumMap<Weeks,Integer> countMap = new EnumMap<Weeks, Integer>(Weeks.class);
        for (Worker worker : workers) {
            for (Weeks day : worker.getAvailableDays()) {
                Integer count = countMap.get(day);
                countMap.put(day,count==null?1:count+1);
            }
        }
        Set<Weeks> days5 = EnumSet.noneOf(Weeks.class);
        for (Map.Entry<Weeks,Integer> entry:countMap.entrySet()){
            if (entry.getValue()>=2){
                days5.add(entry.getKey());
            }
        }
        System.out.println(days5); // [TUESDAY, THURSDAY]
    }

实现原理:位向量
所谓位向量就是用一个位表示一个元素的状态,用一组位表示一个集合的状态,每个位对应一个元素,而状态只有两种.
对于之前的枚举类Week,它有7个枚举值,一个Week的集合就可以用一个字节byte表示,最高位不用,设为0,最右边的位对应顺序最小的枚举值,从右到左,每位对应一个枚举值,1表示包含该元素,0表示不含该元素.
对应的整数是23.
在这里插入图片描述

具体add.remove等方法就不详细介绍了,可以去看源码,涉及到一些位移操作,取反操作,按位或,按位与等操作

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值