如何使用EnumSet实现基于bit field的enum set?

如果我们在使用有一个枚举类型时,每次会用到其一项或多项(也就是enum set)时,会怎么做呢?

在Java没有引入EnumSet之前,有一种叫int enum pattern(assigning a different power of 2 to each constant)的做法,例如一个字体的style的枚举会写成

public static final int STYLE_BOLD = 1 << 0; // 1
public static final int STYLE_ITALIC = 1 << 1; // 2
public static final int STYLE_UNDERLINE = 1 << 2; // 4
public static final int STYLE_STRIKETHROUGH = 1 << 3; // 8

在使用的时候,我们用按位与(|),就可以combine several constants into a set

text.applyStyles(STYLE_BOLD | STYLE_ITALIC);

不过这种做法已经是obsolete了,可以用EnumSet来替代,and it is more concise and flexible,至于效率方面,完全没有下降,因为EnumSet内部实现也是用bitwise的。

那么上面的例子就可以写成这样

// EnumSet - a modern replacement for bit fields
public class Text {
public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }
// Any Set could be passed in, but EnumSet is clearly best
text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
}


如果我们要把这个EnumSet存储到数据库怎么办呢?其实这样的case还是挺多的,我们经常会有flag这样的column,来表示(is or is not),如果这个flag只存储一个(1 或 0 ) 虽然看起来很简单,但是很浪费而且没有扩展性,overtime,新需求也许需要个flagB,  那么你又要加一个新column,flagC,flagD.... frustrated

如果我们把EnumSet直接存到数据库,上面的问题就可以解决了,怎么存呢?看看EnumSet的源码,就知道其实它是用一个long elements来represent 这个set的, 那么我们需要写个工具类能在long和EnumSet之间做转化, 这样就可以存取数据库了。

long转EnumSet

	/**
	 * Create an enum set by parsing the specified value based on the specified enum type
	 * 
	 * @param originalEnumType the class object of the enum type
	 * @param elements The 2^k bit indicates the presence of the element in this set
	 * @return
	 */
	public static <T extends Enum<T>> EnumSet<T> parseEnumSet(Class<T> originalEnumType, long elements){
		EnumSet<T> resultEnumSet = EnumSet.allOf(originalEnumType);
		for (T element : resultEnumSet){
			if ((elements & (1L << element.ordinal())) == 0)//Check if it is 1 at bit 2^K
				resultEnumSet.remove(element);
		}
		return resultEnumSet;		
	}

EnumSet转long

	/**
	 * Calculate the elements' long value from a given enum set
	 * 
	 * @param originalEnumSet the given enum set
	 * @return long value of elements in this set
	 */
	public static <T extends Enum<T>> long getElementsValue(EnumSet<T> originalEnumSet){		
		if (originalEnumSet == null)
			return 0;
		
		long elements = 0;
		for (T element : originalEnumSet){
			elements |= (1L << element.ordinal());
		}
		return elements;
	}

使用

		//Calculate the long value before storing in DB
		System.out.println(EnumSetUtil.getElementsValue(EnumSet.of(Style.BOLD, Style.TEST, Style.UNDERLINE)));
		
		//Parse the enum set from long value after fetching from DB
		System.out.println(EnumSetUtil.parseEnumSet(Style.class, 10));



<think>我们被要求回答“Java中如何使用enum”,并参考了四个引用。用户意图明确:学习Java枚举的使用方法。引用内容摘要:[1]展示了枚举在异常处理中的应用(自定义异常包含枚举错误码)和枚举在接口中的定义(接口内嵌枚举)。[2]展示了一个完整的枚举定义,包含成员变量、构造方法、普通方法(通过索引获取名称)以及getter/setter。[3]提到了EnumSetEnumMap这两个工具类,强调EnumSet的高性能和位向量实现。[4]说明枚举不能被继承,枚举实例必须显式声明在第一行,且默认是publicstaticfinal。因此,回答应覆盖以下方面:1.基本语法:如何定义枚举,声明枚举常量。2.枚举的高级特性:添加成员变量、构造方法、普通方法。3.枚举工具类:EnumSetEnumMap的使用。4.枚举的使用场景:如单例、状态机、错误码等(结合引用中的例子)。5.注意事项:枚举不能被继承,枚举实例都是单例等。回答结构:1.枚举的基本定义2.枚举的进阶用法(成员变量、方法)3.枚举工具类(EnumSet,EnumMap)4.枚举的常见应用场景5.注意事项同时,按照要求,末尾生成相关问题。</think>### Java 枚举(enum使用详解 枚举是 Java 5 引入的特殊类,用于定义固定数量的常量集合。以下是核心用法和场景: --- #### 1. **基础定义与使用** ```java public enum Season { SPRING, // 默认public static final SUMMER, AUTUMN, WINTER; // 分号可选(无成员时) } // 使用示例 Season current = Season.SUMMER; System.out.println(current); // 输出: SUMMER ``` - 每个枚举值都是类的实例[^4] - 默认继承 `java.lang.Enum`,不可被继承[^4] - 可通过 `values()` 获取所有枚举值:`Season[] seasons = Season.values();` --- #### 2. **添加成员变量和方法** ```java public enum ErrorCodeEnum { // 枚举值带参数(调用构造方法) ILLEGAL_ARGS(100, "参数非法"), DATA_NOT_FOUND(404, "数据不存在"); // 成员变量 private final int code; private final String msg; // 构造方法(必须private) private ErrorCodeEnum(int code, String msg) { this.code = code; this.msg = msg; } // 成员方法 public int getCode() { return code; } public String getMessage() { return msg; } } // 使用示例 ErrorCodeEnum error = ErrorCodeEnum.ILLEGAL_ARGS; System.out.println(error.getCode()); // 输出: 100 System.out.println(error.getMessage()); // 输出: "参数非法" ``` - 构造方法必须是 `private`[^2] - 成员变量推荐用 `final` 保证不可变 --- #### 3. **在异常处理中的应用** ```java // 自定义异常类 public class WangException extends RuntimeException { private final ErrorCodeEnum errorCode; public WangException(ErrorCodeEnum errorCode) { super(errorCode.getMessage()); this.errorCode = errorCode; } public ErrorCodeEnum getErrorCode() { return errorCode; } } // 使用场景 try { throw new WangException(ErrorCodeEnum.ILLEGAL_ARGS); } catch (WangException e) { System.out.println("错误码: " + e.getErrorCode().getCode()); System.out.println("错误信息: " + e.getMessage()); } ``` - 结合枚举实现标准化错误码管理[^1] --- #### 4. **高级工具类** ##### (1) EnumSet(高效集合) ```java EnumSet<Season> busySeasons = EnumSet.of(Season.SUMMER, Season.WINTER); System.out.println(busySeasons.contains(Season.SUMMER)); // true ``` - 基于位向量实现,性能极高[^3] - 适用于状态组合 ##### (2) EnumMap(键为枚举的Map) ```java EnumMap<Season, String> activityMap = new EnumMap<>(Season.class); activityMap.put(Season.SPRING, "Planting"); activityMap.put(Season.SUMMER, "Swimming"); ``` --- #### 5. **枚举嵌套与接口** ```java interface Color { enum Green { LIGHT_GREEN, DARK_GREEN } enum Red { LIGHT_RED, DARK_RED } } // 使用 System.out.println(Color.Green.DARK_GREEN.ordinal()); // 输出索引值[^1] ``` --- #### 6. **常用方法** | 方法 | 作用 | 示例 | |---------------------|------------------------------|-------------------------------| | `name()` | 获取枚举名称 | `Season.SPRING.name()` | | `ordinal()` | 获取声明顺序(从0开始) | `Season.SUMMER.ordinal()` | | `valueOf(String)` | 根据名称获取枚举实例 | `Season.valueOf("SPRING")` | | `compareTo()` | 比较枚举顺序 | `SPRING.compareTo(SUMMER)` | --- #### ⚠️ **关键注意事项** 1. 枚举构造方法必须是 `private` 2. 枚举值必须在类首行声明[^4] 3. 枚举不可被继承(final 类) 4. 所有枚举值默认 `public static final` 5. 可通过实现接口扩展功能(替代继承) > 枚举是创建单例模式的最佳实践之一(JVM 保证线程安全)[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值