hadoop 中对Vlong 和 Vint的压缩方法

时间:2021-09-09 07:28:13

hadoop 中对java的基本类型进行了writeable的封装,并且所有这些writeable都是继承自WritableComparable的,都是可比较的;并且,它们都有对应的get() 和 set()方法,

其中对整型(int 和 long)进行编码的时候,有固定长度格式(intWritable和LongWritable)和可变长度格式(VIntWritable 和 VLongWritable),其中VIntWritable和VLongWritable的编码规则是一样的,

所以VIntWritable的输出可以用VLongWritable读入。其实在VIntWritable的write(DataOUtput out) 与 readFields(DataInput in)中分别调用的是WritableUtils.writeVint(out)和WritableUtils.readVint(int),

而writeVint()和readVint()方法也只是简单的调用了writeVLong和readVLong方法。通过writeVInt()写入的数据自然可以通过readVLong()来进行读人;编码规则如下:

其基本思想  主要思想是大的负数的压缩 ,先反码操作,将负数取反,负数一旦取反,前面的字节就有可能变零了。 然后没八位一组截取成 一个字节

写入

  1. public static void writeVLong(DataOutput stream, long i) throws IOException {
  2. // 如果在一个字节可以表示的范围内 直接返回
  3. if (i >= -112 && i <= 127) {
  4. stream.writeByte((byte)i);
  5. return;
  6. }
  7. //把负数变成正数
  8. int len = -112;
  9. if (i < 0) {
  10. i ^= -1L; // take one's complement'
  11. len = -120;
  12. }
  13. //判断正数有几个位数 通过右移实现
  14. long tmp = i;
  15. while (tmp != 0) {
  16. tmp = tmp >> 8;
  17. len--;
  18. }
  19. // 写入第一个字节 该字节标识 这个数十正数还是负数 以及接下来有几个字节属于这个数
  20. stream.writeByte((byte)len);
  21. // 判断需要几个字节表示该数
  22. len = (len < -120) ? -(len + 120) : -(len + 112);
  23. //以每八位一组截取 成一个字节
  24. for (int idx = len; idx != 0; idx--) {
  25. int shiftbits = (idx - 1) * 8;
  26. long mask = 0xFFL << shiftbits;
  27. stream.writeByte((byte)((i & mask) >> shiftbits));
  28. }
  29. }

读取

  1. public static long readVLong(byte[] bytes, int start) throws IOException {
  2. int len = bytes[start];
  3. if (len >= -112) {
  4. return len;
  5. }
  6. boolean isNegative = (len < -120);
  7. len = isNegative ? -(len + 120) : -(len + 112);
  8. if (start+1+len>bytes.length)
  9. throw new IOException(
  10. "Not enough number of bytes for a zero-compressed integer");
  11. long i = 0;
  12. for (int idx = 0; idx < len; idx++) {
  13. i = i << 8;
  14. i = i | (bytes[start+1+idx] & 0xFF);
  15. }
  16. return (isNegative ? (i ^ -1L) : i);
  17. }