1、概述
枚举(enum)是一种特殊的类,是 JDK 1.5 中引入的新特性,用于表示一组固定的常量。枚举使得代码更加清晰、类型安全且易于维护。
1.1 未使用枚举时定义一组常量的缺点
1.2 使用枚举的优点
可以将常量组织起来,统一进行管理。使得代码更加清晰、类型安全且易于维护。
1.3 枚举类型的本质
一种受限制的类,不能继承,并且具有自己的方法。创建enum时,编译器会为你生成一个相关的类,这个类继承自 java.lang.Enum。
1.4 枚举类的特点
- 1)、枚举类不能实例化,构造方法访问权限是私有的,否则不会通过编译。
- 2)、枚举类不能继承其他类或枚举,也不能被继承,但是可以实现接口。
- 3)、枚举类不能作为数据类型(如方法参数)。
- 4)、枚举类可以声明抽象方法,每一个枚举常量都要实现该抽象方法。
2、枚举类的基本定义
使用 enum 关键字定义一个枚举类,类中可以包含字段、方法以及构造函数。
2.1 语法格式:访问修饰符 enum 枚举类型名称 {枚举值1, 枚举值2, ···; 枚举类中的方法}
- 1)、基础枚举类:
public enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY; }
- 2)、 包含字段、方法和构造函数的枚举类:
public enum WeekDay { MONDAY("Monday", "星期一"), TUESDAY("Tuesday", "星期二"), WEDNESDAY("Wednesday", "星期三"), THURSDAY("Thursday", "星期四"), FRIDAY("Friday", "星期五"), SATURDAY("Saturday", "星期六"), SUNDAY("Sunday", "星期日"); private String code; private String value; WeekDay(String code, String value) { this.code = code; this.value = value; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } }
2.2 枚举类的构造函数
枚举的构造方法访问权限是私有的,否则不会通过编译,防止外部创建枚举实例。每个枚举常量在声明时,实际上是调用了这个私有构造函数。
2.3 枚举类的使用
与使用普通类一样使用枚举类。可以遍历枚举常量、访问枚举字段和方法。
// 基本使用
WeekDay monday = WeekDay.MONDAY;
System.out.println(monday instanceof Enum); // 输出true
System.out.println(monday); // 输出MONDAY
System.out.println(monday.getCode() + "-" + monday.getValue()); // 输出MONDAY-星期一
3、枚举类的内置方法
3.1 枚举类的方法:通过枚举类.方法名()进行调用。
- 1)、values():返回包含所有枚举常量的数组。
- 2)、valueOf(String name):将字符串转换为对应的枚举常量。若字符串与枚举常量名称不匹配,转换失败会报IllegalArgumentException异常。
// 枚举类方法
// 1)、values(): 返回包含所有枚举常量的数组。
WeekDay[] values = WeekDay.values();
Arrays.stream(values).forEach(item -> System.out.printf(item.getValue() + " ")); // 输出星期一 星期二 星期三 星期四 星期五 星期六 星期日
// 2)、valueOf(String name): 将字符串转换为对应的枚举常量。
WeekDay sunDay1 = WeekDay.valueOf("SUNDAY");
System.out.println(sunDay1); // 输出SUNDAY
WeekDay sunDay2 = WeekDay.valueOf("SunDay");
System.out.println(sunDay2); // 转换失败,抛出异常IllegalArgumentException
3.2 枚举常量的方法:通过枚举常量.方法名进行调用。
- 1)、final String name(): 返回枚举常量的名称。
- 2)、int ordinal():返回枚举常量在声明中的位置(从0开始)。
- 3)、String toString():返回枚举常量的名称,通常与 name() 方法相同。
- 4)、final int compareTo(E o):比较两个枚举常量在声明中的顺序。
- Ⅰ、如果返回值 < 0,表示枚举常量的顺序小于枚举常量。
- Ⅱ、如果返回值 = 0,表示枚举常量的顺序等于枚举常量。
- Ⅲ、如果返回值 > 0,表示枚举常量的顺序大于枚举常量。
- 5)、final Class<E> getDeclaringClass():可以获取代表当前枚举类型的Class对象,被final关键字修饰,不能被重写。
// 枚举常量方法
WeekDay monday = WeekDay.MONDAY;
WeekDay tuesday = WeekDay.TUESDAY;
WeekDay wednesday = WeekDay.WEDNESDAY;
WeekDay thursday = WeekDay.THURSDAY;
WeekDay friday = WeekDay.FRIDAY;
WeekDay saturday = WeekDay.SATURDAY;
WeekDay sunday = WeekDay.SUNDAY;
// 1)、final String name(): 返回枚举常量的名称。
System.out.println(monday.name()); // 输出MONDAY
System.out.println(friday.name()); // 输出FRIDAY
// 2)、int ordinal():返回枚举常量在声明中的位置(从0开始)。
System.out.println(tuesday.ordinal()); // 输出1
System.out.println(friday.ordinal()); // 输出4
System.out.println(saturday.ordinal()); // 输出5
// 3)、String toString(): 返回枚举常量的名称。
System.out.println(wednesday); // 输出WEDNESDAY
System.out.println(wednesday.name()); // 输出WEDNESDAY
System.out.println(wednesday.toString()); // 输出WEDNESDAY
// 4)、final int compareTo(E o):比较两个枚举常量在声明中的顺序。
System.out.println(thursday.compareTo(wednesday)); // 输出1
System.out.println(thursday.compareTo(friday)); // 输出-1
System.out.println(thursday.compareTo(WeekDay.THURSDAY)); // 输出0
// 5)、final Class<E> getDeclaringClass():获取代表当前枚举类型的Class对象
Class<WeekDay> declaringClass = sunday.getDeclaringClass();
System.out.println(declaringClass); // 输出class javase.enumApi.WeekDay
3.3 枚举类的抽象方法:枚举类可以声明抽象方法,每一个枚举常量都要实现该抽象方法。
具体代码如下:
public enum Subject {
CHINESE("Mr.小王") {
@Override
int score() {
return 66;
}
},
MATH("Mr.小李") {
@Override
int score() {
return 88;
}
},
ENGLISH("小小小张") {
@Override
int score() {
return 77;
}
};
private String teacherName;
private Subject(String teacherName) {
if (teacherName != null && !teacherName.isEmpty())
this.teacherName = teacherName;
}
{
teacherName = "霸王龙";
}
abstract int score();
}
4、枚举类与switch语句
枚举与 switch 语句配合使用非常方便,可以使代码更加简洁和易读。
// 枚举类与switch语句结合使用
WeekDay friday = WeekDay.FRIDAY;
String weekDay = "";
switch (friday) {
case MONDAY:
weekDay = WeekDay.MONDAY.getValue();
break;
case WEDNESDAY:
weekDay = WeekDay.WEDNESDAY.getValue();
break;
case FRIDAY:
weekDay = WeekDay.FRIDAY.getValue();
break;
default:
break;
}
System.out.println(weekDay); // 输出星期五
5、枚举常用的工具类库
主要为EnumSet和EnumMap这两个工具类库。
5.1 EnumSet
EnumSet是一个抽象类,实现了Set接口,是枚举专用的Set接口的抽象实现,是添加枚举类元素的专用集合类,是一个有序集合。只能存储同一枚举类型中的枚举常量,不能存储其他类型的对象。不允许null值,否则会抛出空指针异常。
5.1.1 特点
- 1)、高效性:EnumSet内部使用位向量(bit vector)来表示集合,这使得它在空间和时间复杂度上非常高效。
- 2)、有序性:EnumSet是一个有序集合,元素按枚举常量在枚举类中定义的顺序排列。
- 3)、专用性:EnumSet只能用于存储枚举类型的元素,且所有元素必须来自同一枚举类型。
5.1.2 创建EnumSet集合
EnumSet是抽象类,不能直接实例化,但提供了静态方法用于构造EnumSet对象。
- 1)、<E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType):构造一个空的EnumSet集合。
- 2)、<E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType):构造一个包含指定枚举类中所有枚举项的EnumSet集合。
- 3)、<E extends Enum<E>> EnumSet<E> of(E e1, E e2, ...):构造包含指定元素的EnumSet集合,可以包含1-5个元素,也可以使用可变参数构造包含多个元素的集合。
- 4)、<E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s):根据指定EnumSet集合来构造一个包含该集合中所有元素的EnumSet集合。
- 5)、<E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c):根据指定Collection集合(集合中的元素必须是同一枚举类型的枚举常量)来构造一个包含该集合中所有元素的EnumSet集合。
- 6)、<E extends Enum<E>> EnumSet<E> range(E from, E to):根据传入的枚举常量返回一个包含[from.ordinal(), to.ordinal()]的所有枚举常量的Set集合,如果from.ordinal()大于to.ordinal(),则抛出IllegalArgumentException异常
- 7)、<E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s):根据传入的EnumSet集合返回在该集合所在的枚举类中所有枚举常量的补集的EnumSet集合(枚举类中有而传入的EnumSet集合中没有的枚举常量所组成的集合)。
具体代码如下:
// 1、创建EnumSet集合
// 1)、EnumSet<E> noneOf(Class<E> elementType):构造一个空的集合。
EnumSet<WeekDay> emptyEnumSet = EnumSet.noneOf(WeekDay.class);
System.out.println("构造一个空的集合:" + emptyEnumSet);
// 2)、EnumSet<E> allOf(Class<E> elementType):构造一个包含枚举类中所有枚举项的集合。
EnumSet<WeekDay> allOfEnumSet = EnumSet.allOf(WeekDay.class);
System.out.println("构造一个包含枚举类中所有枚举项的集合:" + allOfEnumSet);
// 3)、EnumSet<E> of(E e1, E e2, ...):构造包含指定元素的集合,可以包含1到5个元素。
EnumSet<WeekDay> weekDayEnumSet = EnumSet.of(WeekDay.MONDAY, WeekDay.WEDNESDAY, WeekDay.FRIDAY);
System.out.println("构造包含指定元素的集合,可以包含1到5个元素:" + weekDayEnumSet);
// 4)、EnumSet<E> copyOf(EnumSet<E> s):构造包含参数中所有元素的集合。
EnumSet<WeekDay> copyOfEnumSet = EnumSet.copyOf(weekDayEnumSet);
System.out.println("根据指定EnumSet集合构造包含该集合中所有元素的集合:" + copyOfEnumSet);
// 5)、EnumSet<E> copyOf(Collection<E> c):构造包含参数中所有元素的集合,但参数集合中的元素必须是同一枚举类型的枚举常量。
List<WeekDay> weekDayList = Arrays.asList(WeekDay.TUESDAY, WeekDay.THURSDAY, WeekDay.SATURDAY);
EnumSet<WeekDay> copyOfEnumSet2 = EnumSet.copyOf(weekDayList);
System.out.println("根据指定Collection集合构造包含该集合中所有元素的集合:" + copyOfEnumSet2);
// 6)、 EnumSet<E> range(E from, E to):返回[枚举常量form, 枚举常量to]之间的所有枚举常量的集合
EnumSet<WeekDay> rangeEnumSet1 = EnumSet.range(WeekDay.MONDAY, WeekDay.FRIDAY);
System.out.println("获取星期一到星期五之间的枚举常量(闭区间[]):" + rangeEnumSet1);
// EnumSet<WeekDay> rangeEnumSet2 = EnumSet.range(WeekDay.SATURDAY, WeekDay.WEDNESDAY);
// System.out.println(rangeEnumSet2); // from.ordinal()大于to.ordinal()抛出IllegalArgumentException异常
// 7)、<E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s):返回指定EnumSet集合在所在枚举类中枚举常量的补集
EnumSet<WeekDay> complementOfEnumSet = EnumSet.complementOf(rangeEnumSet1);
System.out.println("获取星期一到星期五之间的枚举常量的补集:" + complementOfEnumSet);
5.1.3 常用方法
包括添加元素、删除元素、检查元素是否存在等,这些操作都是基于位运算实现的,因此效率非常高。
- 1)、添加元素:分为单个添加与批量添加。如果元素已经存在,则添加操作不会改变集合。
- Ⅰ、boolean add(E e):向EnumSet集合中添加单个元素。
- Ⅱ、boolean addAll(Collection<? extends E> c):将指定集合中的所有元素添加到当前EnumSet集合中。
- 2)、删除元素:分为单个删除、批量删除、按条件删除,返回的都是boolean值。
- Ⅰ、boolean remove(Object o):删除单个元素。
- Ⅱ、boolean removeIf(Predicate<? super E> filter):根据条件删除元素。
- Ⅲ、boolean removeAll(Collection<?> c):批量删除与指定集合中相同的元素。
- 3)、检查元素是否存在:分为单个检查、批量检查,返回boolean值。
- Ⅰ、boolean contains(Object o):检查当前EnumSet中是否包含指定的元素。
- Ⅱ、boolean containsAll(Collection<?> c):检查当前EnumSet中是否包含指定集合中的全部元素。
具体代码如下:
// 2、操作方法
// 添加元素
// 1)、boolean add(E e):向EnumSet集合中添加元素,如果元素已经存在,则添加操作不会改变集合。
EnumSet<WeekDay> enumSet1 = EnumSet.noneOf(WeekDay.class);
enumSet1.add(WeekDay.MONDAY);
enumSet1.add(WeekDay.FRIDAY);
enumSet1.add(WeekDay.MONDAY);
System.out.println(enumSet1); // 输出[MONDAY, FRIDAY]
// 2)、boolean addAll(Collection<? extends E> c):将指定集合中的元素添加到当前EnumSet集合中
EnumSet<WeekDay> enumSet2 = EnumSet.noneOf(WeekDay.class);
enumSet2.addAll(EnumSet.range(WeekDay.MONDAY, WeekDay.FRIDAY));
System.out.println(enumSet2); // 输出[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY]
// 删除元素
EnumSet<WeekDay> weekDayEnumSet = EnumSet.allOf(WeekDay.class);
System.out.println("初始集合:" + weekDayEnumSet); // 输出[MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
// 1)、boolean remove(Object o):删除单个元素
weekDayEnumSet.remove(WeekDay.MONDAY);
System.out.println("删除单个元素(MONDAY):" + weekDayEnumSet); // 输出[TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]
// 2)、boolean removeIf(Predicate<? super E> filter):根据条件删除元素
weekDayEnumSet.removeIf(weekDay -> weekDay.ordinal() > 4);
System.out.println("删除枚举常量排序>4的元素:" + weekDayEnumSet); // 输出[TUESDAY, WEDNESDAY, THURSDAY, FRIDAY]
// 3)、boolean removeAll(Collection<?> c)
weekDayEnumSet.removeAll(EnumSet.of(WeekDay.TUESDAY, WeekDay.WEDNESDAY));
System.out.println("删除与指定集合中相同的元素:" + weekDayEnumSet); // 输出[THURSDAY, FRIDAY]
// 判断元素是否存在
EnumSet<WeekDay> enumSet = EnumSet.range(WeekDay.TUESDAY, WeekDay.FRIDAY);
System.out.println("初始集合:" + enumSet); // 输出[TUESDAY, WEDNESDAY, THURSDAY, FRIDAY]
// 1)、boolean contains(Object o):检查当前EnumSet中是否包含指定的元素。
System.out.println("当前集合是否包含枚举常量MONDAY:" + enumSet.contains(WeekDay.MONDAY)); // 输出false
System.out.println("当前集合是否包含枚举常量WEDNESDAY:" + enumSet.contains(WeekDay.WEDNESDAY)); // 输出true
// 2)、boolean containsAll(Collection<?> c):检查当前EnumSet中是否包含指定集合中的全部元素
EnumSet<WeekDay> enumSet3 = EnumSet.of(WeekDay.TUESDAY, WeekDay.SATURDAY);
EnumSet<WeekDay> enumSet4 = EnumSet.of(WeekDay.TUESDAY, WeekDay.WEDNESDAY);
System.out.println("当前集合是否包含enumSet3中所有元素:" + enumSet.containsAll(enumSet3)); // 输出false
System.out.println("当前集合是否包含enumSet4中所有元素:" + enumSet.containsAll(enumSet4)); // 输出true
5.2 EnumMap
EnumMap是一个具体类,它实现了Map接口,是枚举专用的Map接口实现类,专门用于存储键为枚举类型的数据。可以方便地存储、查找和删除枚举类型作为键的数据。在需要高效操作枚举类型键的Map的场景中,EnumMap是一个非常有用的工具。
5.2.1 特点
- 1)、内存优化:EnumMap内部不像HashMap那样使用哈希桶,而是直接使用了枚举值的序号(通过枚举的ordinal()方法获取)作为数组的索引来存储数据。这意味着在内存分配上,EnumMap会比HashMap更加高效,因为它不需要额外的空间来存储哈希码。
- 2)、高效性:由于EnumMap是根据枚举值的序号来存储数据,查找操作几乎是常数时间复杂度(O(1)),而不需要像HashMap那样计算哈希值,从而减少了性能开销。
- 3)、类型安全:EnumMap要求键必须是枚举类型,这就避免了错误地使用不合适的键类型,增强了类型安全。
- 4)、有序性:EnumMap中的键值对是有序的,它们的顺序是根据枚举类型中定义的顺序来排序的。
5.2.2 实现原理
- 1)、使用一个数组来存储Map中的键值对,数组的长度等于枚举类型中定义的值的数量。
- 2)、数组的索引对应枚举值的序号(通过ordinal()方法获取),数组的元素存储对应的值。
- 3)、由于枚举值本身是固定的,数量非常有限,因此这种实现方式非常高效。
5.2.3 创建EnumSet集合
- 1)、EnumMap(Class<K> keyType):根据枚举类型创建一个空的EnumMap集合。
- 2)、EnumMap(EnumMap<K, ? extends V> m):根据传入的EnumMap创建一个副本EnumMap。
- 3)、EnumMap(Map<K, ? extends V> m):根据传入的Map创建一个副本EnumMap。
具体代码如下:
// 构造方法
// 1、根据枚举类型创建一个空的EnumMap集合
EnumMap<WeekDay, String> enumMap1 = new EnumMap<>(WeekDay.class);
enumMap1.put(WeekDay.MONDAY, "上班第一天");
enumMap1.put(WeekDay.FRIDAY, "上班第二天");
System.out.println("根据枚举类型创建一个空的EnumMap集合:" + enumMap1); // 输出{MONDAY=上班第一天, FRIDAY=上班第二天}
// 2、根据传入EnumMap集合创建副本EnumMap集合
EnumMap<WeekDay, String> enumMap2 = new EnumMap<>(enumMap1);
System.out.println("根据传入EnumMap集合创建副本:" + enumMap2); // 输出{MONDAY=上班第一天, FRIDAY=上班第二天}
// 3、根据普通的Map集合创建EnumMap集合
HashMap<WeekDay, String> hashMap = new HashMap<>();
hashMap.put(WeekDay.SATURDAY, "休息第一天");
hashMap.put(WeekDay.SUNDAY, "休息第二天");
EnumMap<WeekDay, String> enumMap3 = new EnumMap<>(hashMap);
System.out.println("根据普通的Map集合创建EnumMap集合:" + enumMap3); // 输出{SATURDAY=休息第一天, SUNDAY=休息第二天}
5.2.4 常用方法
- 1)、V put(K key, V value):向EnumMap中添加一个键值对。
- 2)、V get(Object key):根据键名获取对应的键值。
- 3)、V remove(Object key):根据键名删除对应的键值对,返回被删除元素的键值。
- 4)、boolean containsKey(Object key):检查EnumMap中是否包含指定的键。
- 5)、int size():返回EnumMap中键值对的数量。
具体代码如下:
// 常用方法
EnumMap<WeekDay, String> enumMap1 = new EnumMap<>(WeekDay.class);
System.out.println("初始EnumMap集合:" + enumMap1); // 输出{}
// 1、添加元素(键值对)
enumMap1.put(WeekDay.MONDAY, "上班第一天");
enumMap1.put(WeekDay.FRIDAY, "上班最后一天");
System.out.println("添加元素后的EnumMap集合:" + enumMap1); // 输出{MONDAY=上班第一天, FRIDAY=上班最后一天}
// 2、根据键名获取对应的键值
String s = enumMap1.get(WeekDay.MONDAY);
System.out.println("键名为MONDAY的元素的值为:" + s); // 键名为MONDAY的元素的值为:上班第一天
// 3、根据键名删除对应的键值对
String remove = enumMap1.remove(WeekDay.MONDAY);
System.out.println("被删除元素的键值:" + remove); // 被删除元素的键值:上班第一天
System.out.println("删除元素后的EnumMap集合:" + enumMap1); // 删除元素后的EnumMap集合:{FRIDAY=上班最后一天}
// 4、判断EnumMap中是否包含指定的键名
boolean containsKeyOfTHURSDAY = enumMap1.containsKey(WeekDay.THURSDAY);
System.out.println("enumMap1集合中是否有键名为MONDAY的键值对:" + containsKeyOfTHURSDAY); // 输出false
boolean containsKeyOfFRIDAY = enumMap1.containsKey(WeekDay.FRIDAY);
System.out.println("enumMap1集合中是否有键名为FRIDAY的键值对:" + containsKeyOfFRIDAY); // 输出true
// 5、获取EnumMap集合中元素的个数
System.out.println("enumMap1集合中元素的个数为:" + enumMap1.size()); // enumMap1集合中元素的个数为:1
标签:Enum,System,EnumSet,枚举,WeekDay,集合,out
From: https://blog.csdn.net/Lweqin/article/details/145115557