Java基本类型的Writable封装
目前Java基本类型
对应的Writable封装
如表所示
所有这些Writable
类都继承自WritableComparable
。也就是说,它们是可比较的。
同时,它们都有get()
和set()
方法,用于获得和设置封装的值。
对整形(int
和long
)进行编码的时候,有固定长度格式(IntWritable
和LongWritable
)和可变长度格式(VIntWritable
和VLongWritable
)两种选择。
固定长度格式的整形,序列化后的数据是定长的,而可变长度格式则是用一种比较灵活的编码方式,对于数之比较小的整形,它们往往比较节省空间。
同时,由于VIntwritable
和VLongWritable
的编码规则是一样的,所以VIntwritable
的输出可以用VLongWritable
读入。
下面以VLongWritable
为例,说明Writable
的Java基本类
封装实现。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | public class VIntWritable implements WritableComparable<VIntWritable> { private int value; public VIntWritable() {} public VIntWritable(int value) { set(value); } /** 设置VIntWritable的值. */ public void set(int value) { this.value = value; } /** 获取VIntWritable的值. */ public int get() { return value; } @Override public void readFields(DataInput in) throws IOException { value = WritableUtils.readVInt(in); } @Override public void write(DataOutput out) throws IOException { WritableUtils.writeVInt(out, value); } /** Returns true iff <code>o</code> is a VIntWritable with the same value. */ @Override public boolean equals(Object o) { if (!(o instanceof VIntWritable)) return false; VIntWritable other = (VIntWritable)o; return this.value == other.value; } @Override public int hashCode() { return value; } /** Compares two VIntWritables. */ @Override public int compareTo(VIntWritable o) { int thisValue = this.value; int thatValue = o.value; return (thisValue < thatValue ? -1 : (thisValue == thatValue ? 0 : 1)); } @Override public String toString() { return Integer.toString(value); } } |
首先,每个Java基本类型
的Writale
封装,其类的内部都包含一个对应基本类型的成员变量value
,get()
和set()
方法就是用来对该变量进行取值/赋值操作的。
而Writable
接口要求的readFields()
和write()
方法,VIntWritable
则是通过调用Writable
工具类中提供的readVInt()
和writeVInt()
读/写数据。方法readVInt()
和writeVInt()
的实现也只是简单调用了readVLong()
和writeVLong()
,所以,通过writeVInt()
写的数据自然可以通过readVLong()
读入。
读:
1 2 3 4 5 6 7 8 | public static int readVInt(DataInput stream) throws IOException { long n = readVLong(stream); if ((n > Integer.MAX_VALUE) || (n < Integer.MIN_VALUE)) { throw new IOException("value too long to fit in integer"); } return (int)n; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public static long readVLong(DataInput stream) throws IOException { byte firstByte = stream.readByte(); int len = decodeVIntSize(firstByte); if (len == 1) { return firstByte; } long i = 0; for (int idx = 0; idx < len-1; idx++) { byte b = stream.readByte(); i = i << 8; i = i | (b & 0xFF); } return (isNegativeVInt(firstByte) ? (i ^ -1L) : i); } |
1 2 3 4 5 6 7 8 9 | public static int decodeVIntSize(byte value) { if (value >= -112) { return 1; } else if (value < -120) { return -119 - value; } return -111 - value; } |
写:
1 2 3 4 | public static void writeVInt(DataOutput stream, int i) throws IOException { writeVLong(stream, i); } |
writeVLong()
方法实现了对整形数值的变长编码,它的编码规则如下:
如果输入的整数大于或等于-112
同时小于或等于127
,那么编码需要1字节
否则,序列化结果的第一个字节,保存了输入整数的符号和后续编码的字节数。
符号和后续字节数依据下面的编码规则(又一个规则):
- 如果是正数,则编码值范围落在
-113
和-120
间(闭区间),后续字节数可以通过-(v+112)
计算。 - 如果是负数,则编码值范围落在
-121
和-128
间(闭区间),后续字节数可以通过-(v+120)
计算。
后续编码将高位在前,写入输入的整数(除去前面全0字节)。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | public static void writeVInt(DataOutput stream, int i) throws IOException { writeVLong(stream, i); } /** * Serializes a long to a binary stream with zero-compressed encoding. * For -112 <= i <= 127, only one byte is used with the actual value. * For other values of i, the first byte value indicates whether the * long is positive or negative, and the number of bytes that follow. * If the first byte value v is between -113 and -120, the following long * is positive, with number of bytes that follow are -(v+112). * If the first byte value v is between -121 and -128, the following long * is negative, with number of bytes that follow are -(v+120). Bytes are * stored in the high-non-zero-byte-first order. * * @param stream 保存序列化结果输出流 * @param i 被序列化的整数 * @throws java.io.IOException */ public static void writeVLong(DataOutput stream, long i) throws IOException { // 处于[-112,127]的整数 if (i >= -112 && i <= 127) { stream.writeByte((byte)i); return; } // 计算情况2的第一个字节 int len = -112; if (i < 0) { i ^= -1L; // take one's complement' len = -120; } long tmp = i; while (tmp != 0) { tmp = tmp >> 8; len--; } stream.writeByte((byte)len); len = (len < -120) ? -(len + 120) : -(len + 112); // 输出后续字节 for (int idx = len; idx != 0; idx--) { int shiftbits = (idx - 1) * 8; long mask = 0xFFL << shiftbits; stream.writeByte((byte)((i & mask) >> shiftbits)); } } |