一、枚举类简介
Java 枚举是一个特殊的类,一般表示一组常量,比如一年的 4 个季节,一年的 12 个月份,一个星期的 7 天,方向有东南西北等。
Java 枚举类使用 enum 关键字来定义,各个常量使用逗号 , 来分割。
例如定义一个颜色的枚举类。
public enum Color {
RED("红色"),
GREEN("绿色"),
BLUE("蓝色");
//枚举中常量的描述
private String description;
Color(String description) {
this.description = description;
}
}
以上枚举类 Color 颜色常量有 RED, GREEN, BLUE,分别表示红色,绿色,蓝色。
使用实例:
// 执行输出结果
public static void main(String[] args)
{
Color c1 = Color.RED;
System.out.println(c1);
}
=====输出结果====
RED
每个枚举都是通过 Class 在内部实现的,且所有的枚举值都是 public static final 的。
以上的枚举类 Color 转化在内部类实现:
class Color
{
public static final Color RED = new Color("红色");
public static final Color BLUE = new Color("蓝色");
public static final Color GREEN = new Color("绿色");
}
1.1 迭代枚举元素
可以使用 for 语句来迭代枚举元素:
public class Test {
public static void main(String[] args) {
for (Color color : Color.values()) {
System.out.println(color);
}
}
}
=====输出结果====
RED
GREEN
BLUE
1.2 values(), ordinal() 和 valueOf() 方法
enum 定义的枚举类默认继承了 java.lang.Enum 类,并实现了 java.lang.Serializable 和 java.lang.Comparable 两个接口。
values(), ordinal() 和 valueOf() 方法位于 java.lang.Enum 类中:
- values() 返回枚举类中所有的值。
- ordinal()方法可以找到每个枚举常量的索引,就像数组索引一样。
- valueOf()方法返回指定字符串值的枚举常量。
public class Test {
public static void main(String[] args) {
// 调用 values()
Color[] arr = Color.values();
// 迭代枚举
for (Color col : arr){
// 查看索引
System.out.println(col + " at index " + col.ordinal());
}
// 使用 valueOf() 返回枚举常量,不存在的会报错 IllegalArgumentException
System.out.println(Color.valueOf("RED"));
System.out.println(Color.valueOf("WHITE"));
}
}
1.3 枚举类成员
枚举跟普通类一样可以用自己的变量、方法和构造函数,构造函数只能使用 private 访问修饰符,所以外部无法调用。
枚举既可以包含具体方法,也可以包含抽象方法。 如果枚举类具有抽象方法,则枚举类的每个实例都必须实现它。
public enum Color {
RED("红色"){
@Override
public String getDescription() {
return RED.description;
}
},
GREEN("绿色") {
@Override
public String getDescription() {
return GREEN.description;
}
},
BLUE("蓝色") {
@Override
public String getDescription() {
return BLUE.description;
}
};
//枚举中常量的描述
private String description;
Color(String description) {
this.description = description;
}
public abstract String getDescription();
public String getDescription2(){
return this.description;
}
public class Test {
public static void main(String[] args) {
// 调用 values()
Color red = Color.RED;
System.out.println(red.getDescription());
System.out.println(red.getDescription2());
}
}
enum类允许我们为其定义抽象方法,然后使每个枚举实例都实现该方法,以便产生不同的行为方式,enum类的实例似乎表现出了多态的特性,可惜的是枚举类型的实例终究不能作为类型传递使用。
无法通过编译,毕竟Color.RED是个实例对象
public void text(Color.RED red){ }
1.4 EnumMap
EnumMap是Map接口的一种实现,专门用于枚举类型的键,所有枚举的键必须来自同一个枚举,EnumMap不允许键为空,允许值为空
public class Test {
public static void main(String[] args) {
// 调用 values()
EnumMap<Color, Object> enumMap = new EnumMap<>(Color.class);
enumMap.put(Color.BLUE,"这是蓝色");
enumMap.put(Color.RED,"这是红色");
enumMap.put(Color.GREEN,"这是绿色");
for (Map.Entry<Color, Object> colorObjectEntry : enumMap.entrySet()) {
System.out.println(colorObjectEntry.getKey() + ": ");
System.out.println(colorObjectEntry.getValue());
}
System.out.println("BLUE:"+enumMap.get(Color.BLUE));
}
}
EnumMap的方法,跟普通的map几乎没有区别,注意与HashMap的主要不同在于构造方法需要传递类型参数和EnumMap保证Key顺序与枚举中的顺序一致。
1.5 EnumSet
EnumSet是与枚举类型一起使用的专用 Set 集合,EnumSet 中所有元素都必须是枚举类型。与其他Set接口的实现类HashSet/TreeSet(内部都是用对应的HashMap/TreeMap实现的)不同的是,EnumSet在内部实现是位向量(稍后分析),它是一种极为高效的位运算操作,由于直接存储和操作都是bit,因此EnumSet空间和时间性能都十分可观,足以媲美传统上基于 int 的“位标志”的运算,重要的是我们可像操作set集合一般来操作位运算,这样使用代码更简单易懂同时又具备类型安全的优势。注意EnumSet不允许使用 null 元素。试图插入 null 元素将抛出 NullPointerException,但试图测试判断是否存在null 元素或移除 null 元素则不会抛出异常,与大多数collection 实现一样,EnumSet不是线程安全的,因此在多线程环境下应该注意数据同步问题,下面先来简单看看EnumSet的使用方式。
EnumSet用法
创建EnumSet并不能使用new关键字,因为它是个抽象类,而应该使用其提供的静态工厂方法,EnumSet的静态工厂方法比较多,如下:
创建一个具有指定元素类型的空EnumSet。
EnumSet<E> noneOf(Class<E> elementType)
//创建一个指定元素类型并包含所有枚举值的EnumSet
<E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType)
// 创建一个包括枚举值中指定范围元素的EnumSet
<E extends Enum<E>> EnumSet<E> range(E from, E to)
// 初始集合包括指定集合的补集
<E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s)
// 创建一个包括参数中所有元素的EnumSet
<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)
//创建一个包含参数容器中的所有元素的EnumSet
<E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s)
<E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c)
enum Color {
GREEN , RED , BLUE , BLACK , YELLOW
}
public class EnumSetDemo {
public static void main(String[] args){
//空集合
EnumSet<Color> enumSet= EnumSet.noneOf(Color.class);
System.out.println("添加前:"+enumSet.toString());
enumSet.add(Color.GREEN);
enumSet.add(Color.RED);
enumSet.add(Color.BLACK);
enumSet.add(Color.BLUE);
enumSet.add(Color.YELLOW);
System.out.println("添加后:"+enumSet.toString());
System.out.println("-----------------------------------");
//使用allOf创建包含所有枚举类型的enumSet,其内部根据Class对象初始化了所有枚举实例
EnumSet<Color> enumSet1= EnumSet.allOf(Color.class);
System.out.println("allOf直接填充:"+enumSet1.toString());
System.out.println("-----------------------------------");
//初始集合包括枚举值中指定范围的元素
EnumSet<Color> enumSet2= EnumSet.range(Color.BLACK,Color.YELLOW);
System.out.println("指定初始化范围:"+enumSet2.toString());
System.out.println("-----------------------------------");
//指定补集,也就是从全部枚举类型中去除参数集合中的元素,如下去掉上述enumSet2的元素
EnumSet<Color> enumSet3= EnumSet.complementOf(enumSet2);
System.out.println("指定补集:"+enumSet3.toString());
System.out.println("-----------------------------------");
//初始化时直接指定元素
EnumSet<Color> enumSet4= EnumSet.of(Color.BLACK);
System.out.println("指定Color.BLACK元素:"+enumSet4.toString());
EnumSet<Color> enumSet5= EnumSet.of(Color.BLACK,Color.GREEN);
System.out.println("指定Color.BLACK和Color.GREEN元素:"+enumSet5.toString());
System.out.println("-----------------------------------");
//复制enumSet5容器的数据作为初始化数据
EnumSet<Color> enumSet6= EnumSet.copyOf(enumSet5);
System.out.println("enumSet6:"+enumSet6.toString());
System.out.println("-----------------------------------");
List<Color> list = new ArrayList<Color>();
list.add(Color.BLACK);
list.add(Color.BLACK);//重复元素
list.add(Color.RED);
list.add(Color.BLUE);
System.out.println("list:"+list.toString());
//使用copyOf(Collection<E> c)
EnumSet enumSet7=EnumSet.copyOf(list);
System.out.println("enumSet7:"+enumSet7.toString());
/**
输出结果:
添加前:[]
添加后:[GREEN, RED, BLUE, BLACK, YELLOW]
-----------------------------------
allOf直接填充:[GREEN, RED, BLUE, BLACK, YELLOW]
-----------------------------------
指定初始化范围:[BLACK, YELLOW]
-----------------------------------
指定补集:[GREEN, RED, BLUE]
-----------------------------------
指定Color.BLACK元素:[BLACK]
指定Color.BLACK和Color.GREEN元素:[GREEN, BLACK]
-----------------------------------
enumSet6:[GREEN, BLACK]
-----------------------------------
list:[BLACK, BLACK, RED, BLUE]
enumSet7:[RED, BLUE, BLACK]
*/
}
}
noneOf(Class elementType)静态方法,主要用于创建一个空的EnumSet集合,传递参数elementType代表的是枚举类型的类型信息,即Class对象。EnumSet allOf(Class elementType)静态方法则是创建一个填充了elementType类型所代表的所有枚举实例,奇怪的是EnumSet提供了多个重载形式的of方法,最后一个接受的的是可变参数,其他重载方法则是固定参数个数,EnumSet之所以这样设计是因为可变参数的运行效率低一些,所有在参数数据不多的情况下,强烈不建议使用传递参数为可变参数的of方法,即EnumSet of(E first, E… rest),其他方法就不分析了,看代码演示即可。至于EnumSet的操作方法,则与set集合是一样的,可以看API即可这也不过多说明。什么时候使用EnumSet比较恰当的,事实上当需要进行位域运算,就可以使用EnumSet提到位域,如下:
public class EnumSetDemo {
//定义位域变量
public static final int TYPE_ONE = 1 << 0 ; //1
public static final int TYPE_TWO = 1 << 1 ; //2
public static final int TYPE_THREE = 1 << 2 ; //4
public static final int TYPE_FOUR = 1 << 3 ; //8
public static void main(String[] args){
//位域运算
int type= TYPE_ONE | TYPE_TWO | TYPE_THREE |TYPE_FOUR;
}
}
EnumSet最有价值的是其内部实现原理,采用的是位向量,它体现出来的是一种高效的数据处理方式,这点很值得我们去学习它。
EnumSet内部实现原理: https://blog.csdn.net/javazejian/article/details/71333103