【Java 枚举 & 集合】枚举类Enum、映射EnumMap、集EnumSet


本文使用 JDK 版本为 JDK1.15

一、枚举Enum

1、概述

一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。

  • Java的枚举本质上是一个特殊的类,它可以通过enum关键字定义。
  • 它可以定义构造器,但是不可被public修饰,所以不能被实例化。
  • 它的构造器只是在构造枚举值得时候被调用。
  • 它的内部也可以定义变量、方法。

2、介绍

枚举一般用于选择语句中,它提供的API如下:
API

为方便测试,我们定义这样的枚举类型:

public enum Color {
	WHITE,
	BLUE,
	GREEN,
	RED,
	YELLOW,
	BLACK	//这里省略了分号,如果后续需定义其他成员,需加上分号
}

① valueOf

枚举一般应用于switch...case...default语句中,配合内置的valueOf方法提取字符串的值,具体使用案例如下:

public void whatColor(){
	switch (Color.valueOf("YELLOW")){
		case YELLOW:
			System.out.println("黄色");
			break;
		default:
			System.out.println("其他颜色");
			break;
	}
}

运行结果

  • 当我们试图将一个 不存在的元素 的字符串作为 参数 时,会抛出异常:

java.lang.IllegalArgumentException: No enum constant Color.XXX

我们打印输出,进行测试:

System.out.println(Color.valueOf("vovo"));

运行结果

② values

  • 枚举还提供了values方法,通过调用该方法,获取枚举类型内的所有内置元素,返回类型为一个枚举类型的数组(即Color类型):

我们打印输出

System.out.println(Arrays.toString(Color.values()));

运行结果

  • 可以看到,我们按顺序取到了所有的元素值。
  • 那么,枚举类型的本质到底是什么?为什么没有返回 Object 类型 或是 String 类型 的数组?

3、分析

  • 为了探寻,枚举类型的本质,我们使用javap指令,对 Color.java 文件进行反编译:
    反编译结果
  • 枚举类型本质上是一个继承自lang包下,Enum类的派生类。
  • 它默认拥有一个 values 数组、valueOf 方法,以及一个静态代码块。
  • 枚举类型的元素,本质上是与其相同类型的静态常量,只是名称有所不同 ,我们可以通过其名称获取相应的值。

※ 模仿一个枚举类

//Enum 类 不可被 我们随意 继承
public final class Color /*extends Enum<Color>*/{
    public static final Color WHITE;
    public static final Color BLUE;
    public static final Color GREEN;
    public static final Color RED;
    public static final Color YELLOW;
    public static final Color BLACK;

    static{
        WHITE = new Color("WHITE");
        BLUE = new Color("BLUE");
        GREEN = new Color("GREEN");
        RED = new Color("RED");
        YELLOW = new Color("YELLOW");
        BLACK = new Color("BLACK");
    }

	//定义变量color
    private final String color;
    //私有的构造器
    private Color(String color) {
        this.color = color;
    }
    //静态方法 values();
    public static Color[] values() {
        return new Color[] {WHITE,BLUE,GREEN,RED,YELLOW,BLACK};
    }
	//静态方法 valueOf(String color);
    public static Color valueOf(String color) {
        switch (color) {
            case "WHITE":
                return Color.WHITE;
            case "BLUE":
                return Color.BLUE;
            case "GREEN":
                return Color.GREEN;
            case "RED":
                return Color.RED;
            case "YELLOW":
                return Color.YELLOW;
            case "BLACK":
                return Color.BLACK;
            default:
                throw new IllegalArgumentException("No enum constant: " + Color.class.getName() + "." + color);
        }
    }
    //为了防止其打印地址,我们只好重写toString方法,事实上enum类型并没有内置toString方法
    @Override
    public String toString() {
        return this.color;
    }
}

上述类同样可以完成枚举类的功能,只是这样写十分繁琐,使用枚举类型可以帮助我们简化代码,而且看起来十分简洁明了。

二、枚举映射 EnumMap

1、概述

EnumMap是专门为枚举类型量身定做的Map实现,类似地还有EnumSet。虽然使用其它的Map实现(如HashMap)也能完成枚举类型实例到值得映射,但是使用EnumMap会更加高效:它只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以EnumMap使用数组来存放与枚举类型对应的值。这使得EnumMap的效率非常高。

《Effective Java》中作者建议用EnumMap代替叙述索引,最好不要用序数来索引数组,而要使用EnumMap。

EnumMap构造方法采用键类型的Class对象:这是一个有限制的类型令牌,它提供了运行时的泛型信息。
核心构造方法
其中Class<K>作为采用键类型的Class对象,说明EnumMap以枚举类型为键,可取出对应值。

2、使用方法

为方便说明,我们定义如下的星期Enum类,并创建对应的EnumMap:

enum Weekday{
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}

初始化 键值:

这是 Java 中 Map、Set 初始化设定数值的途径,形式上类似于数组{},Map与Set使用双层花括号定义初始数值{{}}

//初始化直接放入键值
Map<Weekday,String> map = new EnumMap<>(Weekday.class){{
	put(Weekday.MONDAY,"星期一");
	put(Weekday.TUESDAY,"星期二");
    put(Weekday.WEDNESDAY,"星期三");
    put(Weekday.THURSDAY,"星期四");
    put(Weekday.FRIDAY,"星期五");
    put(Weekday.SATURDAY,"星期六");
    put(Weekday.SUNDAY,"星期日");
}};

双层花括号的意义?

  • 外层花括号实际是定义了一个匿名内部类 (Anonymous Inner Class)。
  • 内层花括号实际上是一个实例初始化块 (instance initializer block),这个块在内部匿名类构造时被执行。这个块之所以被叫做“实例初始化块”是因为它们被定义在了一个类的实例范围内。

上面代码如果是写在 EnumMapDemo 类中,编译后你会看到会生成 EnumMapDemo$1.class 文件,编译 .java 文件后得到结果:
在这里插入图片描述
剖析这种方式,发现其本质上创建了一个继承 EnumMap 的匿名内部类(内部类概述):

// 第一个{}代表 创建了一个 HashMap 的子类
class EnumMapDemo$1<K extends Enum<K>, V> extends EnumMap {	
	{	// 第二个{}中的代码放到了实例初始化块中去了
		put(Weekday.MONDAY, "星期一");
		put(Weekday.TUESDAY, "星期二");
	}
	EnumMapDemo$1(Class<K> keyType){	//super调用父类构造方法
		super(keyType); 
	}
}

初始化后操作键值:

//先创建枚举映射
Map<Weekday,String> map = new EnumMap<>(Weekday.class);
//后放入键值
map.put(Weekday.MONDAY,"星期一");
map.put(Weekday.TUESDAY,"星期二");
map.put(Weekday.WEDNESDAY,"星期三");
map.put(Weekday.THURSDAY,"星期四");
map.put(Weekday.FRIDAY,"星期五");
map.put(Weekday.SATURDAY,"星期六");
map.put(Weekday.SUNDAY,"星期日");

3、应用场景

情景引入:
现有以 Java 语言编写的服务器、C# 语言 编写的客户端,二者为 C/S架构,均使用 Socket 进行通信。由于语言不相通,须手动构建请求、响应字符串,并指定状态码。
通信解析控制模块

而数字难以识别,这时就可以在服务端使用 Enum 规定请求状态的英文、后以EnumMap<Enum,Integer>指定数字并以此做请求分类,简化我们的操作(图中:通信解析控制模块):

enum Status{
    ACCEPT,	//20
    RESPONSE,//50
    SERVER_CLOSING,//90
    RESOURCES_NOT_FOUND,//60
    ERROR,		//40
    SUCCESS		//70
    //...
}
Map<Status,Integer> statusMap = new EnumMap<>(Status.class){{
	put(Status.ACCEPT,20);
	put(Status.RESPONSE,50);
    put(Status.SERVER_CLOSING,90);
    put(Status.RESOURCES_NOT_FOUND,60);
    put(Status.ERROR,40);
    put(Status.SUCCESS,70);
	//...
}};

也可以将值包装成类,内部附有状态码、方法字符串,利用反射自动寻找、处理业务。

三、枚举集 EnumSet

1、概述

EnumSet 是一个枚举类型元素集的高效实现。枚举类型只有有限个例,所以EnumSet 内部用位序列实现。如果对应的值在集中,则相应的位被置为1。

2、使用方法

EnumSet类没用公共的构造器。可以使用静态工厂方法构造这个集:

enum Weekday{
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}
//全部值
EnumSet<Weekday> always = EnumSet.allOf(Weekday.class);
//无值
EnumSet<Weekday> never= EnumSet.noneOf(Weekday.class);
//范围值
EnumSet<Weekday> workday= EnumSet.range(Weekday.MONDAY,Weekday.FRIDAY);
//选中值
EnumSet<Weekday> mwf = EnumSet.of(Weekday.MONDAY,Weekday.SATURDAY,Weekday.FRIDAY)

⭐ E extends Enum 代表“E是一个枚举类型”。
⭐ 枚举类的特殊性正体现于此:所有的枚举类型都扩展于泛型 Enum 类(参考本文第一节)

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值