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
:
- 導致程序不易理解
- 容易出錯,耗費精力
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
方法的返回值也就是我們狀態存儲的封裝