Java中使用 Long 表示枚举类

本文介绍了一种在Java中利用枚举类结合位操作来高效存储多种状态的方法。通过自定义`LongBitFormat`类,可以将多个状态以位的形式存储在一个`long`变量中,从而节省内存并简化状态管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java中使用 Long 表示枚举类

在日常的开发过程中,很多时候我们需要枚举类(enum)来表示对象的各种状态,并且每个状态往往会关联到指定的数字,如:

    private enum Color {
        RED(11), GREEN(21), YELLOW(31), BLACK(160);
        ...
    };

或者用枚举类来表示一系列状态的转变关系:

    enum Week{
        SUNDAY(1), MONDAY(2), TUESDAY(3), WEDNESDAY(4), THRUSDAY(5), FRIDAY(6), SATRUDAY7);
        ...
    };

那么,如何用最少的存储来实现这类需求,答案很简单,位存储。如 1bit 表示 0,1 两种状态,2bit 表示 00,01,10,11 四种状态,所以我们可以用一个 long 类型(64bit)/int 类型(32bit)存储多种状态,如下图:

这里写图片描述

但是每新建一个枚举类都需要自己操作 bit
1. 导致程序不易理解
2. 容易出错,耗费精力

Hadoop hdfs 的实现中,也遇到类似的问题,它借助于 LongBitFormat.java 类封装了 bit 操作:

    public class LongBitFormat implements Serializable {
    private static final long serialVersionUID = 1L;

    private final String NAME;
    /** Bit offset */
    private final int OFFSET;
    /** Bit length */
    private final int LENGTH;
    /** Minimum value */
    private final long MIN;
    /** Maximum value */
    private final long MAX;
    /** Bit mask */
    private final long MASK;

    public LongBitFormat(String name, LongBitFormat previous, int length,
                         long min) {
        NAME = name;
        OFFSET = previous == null ? 0 : previous.OFFSET + previous.LENGTH;
        LENGTH = length;
        MIN = min;
        MAX = ((-1L) >>> (64 - LENGTH));//移动的位数,右移64-Leng位,相当于保留length位
        MASK = MAX << OFFSET;
    }

    /** Retrieve the value from the record. */
    public long retrieve(long record) {
        return (record & MASK) >>> OFFSET;
    }

    /** Combine the value to the record. */
    public long combine(long value, long record) {
        if (value < MIN) {
            throw new IllegalArgumentException(
                    "Illagal value: " + NAME + " = " + value + " < MIN = " + MIN);
        }
        if (value > MAX) {
            throw new IllegalArgumentException(
                    "Illagal value: " + NAME + " = " + value + " > MAX = " + MAX);
        }
        return (record & ~MASK) | (value << OFFSET);
    }

    public long getMin() {
        return MIN;
    }
}

当然,你也可以实现 IntBigFormat,ShortBitFormat

首先分析该类的构造方法:

        NAME = name;
        OFFSET = previous == null? 0: previous.OFFSET + previous.LENGTH;
        LENGTH = length;
        MIN = min;
        MAX = ((-1L) >>> (64 - LENGTH));//移动的位数,右移64-Leng位,相当于保留length位
        MASK = MAX << OFFSET;

字段:
NAME:状态名,可自定义

OFFSET:该状态在 long 字节中的偏移

LENGTH:用多少位存储该状态关联的数字

MIN:该状态关联的最小值

MAX:该状态关联的最大值

MASK:掩码,(OFFSET~OFFSET+LENGTH - 1) == 1

类方法:
retrieve(long record):获得该状态关联的数字

combine(long value, long record):将一个 value 加到 record 中,例如:将 value 值对应的枚举类存储在 32-40,则先将 32-40bits 清零,再将value 对应的二进制加入到 32-40

那么如何使用该类:

public class LongFormatTest {

    static enum HeaderFormat {
        PREFERRED_BLOCK_SIZE(null, 48, 1),
        REPLICATION(PREFERRED_BLOCK_SIZE.BITS, 12, 1),
        STORAGE_POLICY_ID(REPLICATION.BITS, 4, 0);

        private final LongBitFormat BITS;

        HeaderFormat(LongBitFormat previous, int length, long min) {
            BITS = new LongBitFormat(name(), previous, length, min);
        }

        static short getReplication(long header) {
            return (short) REPLICATION.BITS.retrieve(header);
        }

        static long getPreferredBlockSize(long header) {
            return PREFERRED_BLOCK_SIZE.BITS.retrieve(header);
        }

        static byte getStoragePolicyID(long header) {
            return (byte) STORAGE_POLICY_ID.BITS.retrieve(header);
        }

        static long toLong(long preferredBlockSize, long replication,
                           long storagePolicyID) {
            long h = 0;
            h = PREFERRED_BLOCK_SIZE.BITS.combine(preferredBlockSize, h);
            h = REPLICATION.BITS.combine(replication, h);
            h = STORAGE_POLICY_ID.BITS.combine(storagePolicyID, h);
            return h;
        }
    }

    public static void main(String[] args) {

        long blockSize = 512;
        long replication = 3L;
        long storagePolicyID = 2L;
        long combine = HeaderFormat.toLong(blockSize,replication,storagePolicyID);
        System.out.println("block size:         " + HeaderFormat.getPreferredBlockSize(combine));
        System.out.println("replication:        " + HeaderFormat.getReplication(combine));
        System.out.println("storagePolicyID:    " + HeaderFormat.getStoragePolicyID(combine));
    }
}

tolong 方法的返回值也就是我们状态存储的封装

### 如何在 MySQL 中存储 Java 枚举类型的最佳实践 #### 存储方式的选择 为了有效地将 Java 枚举类型存储到 MySQL 数据库中,通常有两种主要方法:一种是将其作为字符串形式保存;另一种则是以整数编码的形式来表示枚举值。对于前者而言,在表设计阶段可以定义字段的数据类型为 `VARCHAR` 或者更具体的 `ENUM` 类型[^1]。 然而,采用后者即使用数值代表不同状态的方式更为常见也更加高效。这不仅减少了空间占用还便于后续查询操作以及与其他系统的集成工作。此时应确保实体类中的属性被声明成合适的整形变量(如 `Integer`),以便于 MyBatis 映射框架能够顺利地完成对象关系映射过程[^2]。 #### 实体类的设计 当决定利用整数编码方案时,可以在对应的 JPA/MyBatis 的 Entity Bean 定义里加入相应的成员变量用于承载这些枚举值。例如: ```java public class DemoEntity { private Long id; private String name; private Integer status; // getters and setters... } ``` 这里假设存在一个名为 `status` 的列用来记录业务逻辑上的几种固定的状态码,并且它们已经被封装进了某个自定义的 Enum 类内[^3]。 #### 自动化处理机制 为了让应用程序开发者无需每次都显式调用 `.code()` 方法获取当前实例所关联的具体编号,可以通过实现特定接口或者借助第三方库来自动生成这种转换行为。比如 Spring Boot 提供了非常方便的支持,只需简单配置即可让整个流程变得透明而简洁[^4]。 另外值得注意的是,在读取数据库返回的结果集并构建相应领域模型的过程中同样也需要考虑如何正确解析回原始的枚举常量。一般情况下会提供静态工厂函数来进行此类逆向工程的操作,像下面这样: ```java // 假设有一个 AlarmTypeEnum.java 文件包含了 parseCode 静态方法 System.out.println(AlarmTypeEnum.parseCode((short) 2)); ``` 以上就是关于怎样把 Java 枚举类型安全可靠地持久化至 MySQL 表格内的建议与指导。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值