当前位置:编程学习 > 网站相关 >>

hadoop 源码阅读之io篇


从基础的IO包开始阅读。
IO:表示层,将各种数据编码/解码,方便于在网络上传输。
下图是一个大致的结构图,可以看出,大部分的类都是以Writable结
尾的数据结构和基本数据类型。

一、基本数据类型
Hadoop 中,并没有使用java自带的基本类型类(Integer、Float等)而是使用了自己开发的类IntWritable、FloatWritable、BooleanWritable、LongWritable、ByteWritable、BytesWritable、DoubleWritable,他们都实现了接口WritableComparable,接口定义如下:
public interface WritableComparable<T> extends Writable, Comparable<T> {}
Comparable是java.lang包的接口。
Writable接口定义如下
public interface Writable {
  void write(DataOutput out) throws IOException;
  void readFields(DataInput in) throws IOException;
Writable接口是一个序列化对象的接口,能够将数据写入流或者从流中读出。实现了之后,能够进行特定类型数据的异地传输。
除了这些基本类型的定义,还添加了VLongWritable和VIntWritable,V指的是可变长度,例如long型的1实际只需要一个字节的空间,由于是long型的,所以会占用8字节的空间,而VLongWritable中,会根据数值的大小,分配适当的空间(仅分配一个字节),达到节省空间的作用。在基本数据类型的Writable类中,readFields(DataInput in)方法是直接调用in.readLong()(以LongWritable为例),而在VLongWritable与VIntegerWritable中,readFields(DataInputin)方法是使用了静态类WritableUtils中的readVLong(in)方法。WritableUtils是一个工具类,用于提供io中的Writable类的一些静态方法。
下面分析下VLongWritable中的write和readFields方法的实现。
Write方法:
将long型的数根据所占用的字节数写入DataOutput 中,例如-112~ 127,只需要一个字节存储(-128~-113用于做标识了)。其他的数,则需要先指明该数的正负与占用的长度(在第一个字节表示),然后再按照长度存储。
第一个字符中, -113(11110001)到-120(11111000)表示正数,-121(11111000)到-128(10000000)表示负数。
后续的N个字节,表示该数字的N字节。
代码如下:
 
  public static void writeVLong(DataOutput stream, long i) throws IOException {
    if (i >= -112 && i <= 127) {
      stream.writeByte((byte)i);
      return;
    }
    
    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));
    }
  }
 
readVLong方法,若第一个字节大于-112,则仅为一位数,直接返回该值;否则,则判断后面字节的位数,并读出返回。
由于对整数的操作都是以字节方式进行,故都是使用位操作进行。如
i= i << 8;
i= i | (b & 0xFF);
将i左移8位,并将其与新读入的8位数进行或运算,这样,就完成了一个字节数据的写入。
 
  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);
  }
 
  public static int decodeVIntSize(byte value) {
    if (value >= -112) {
      return 1;
    } else if (value < -120) {
      return -119 - value;
    }
    return -111 - value;
  }
 
在上述每个类初始化的时候,都会将自定义的比较器(Comparator)注册进WritableComparator的HashMap中,以供调用。
static {                                        // register this comparator
    WritableComparator.define(FloatWritable.class, new Comparator());
  }
下图是这些基本数据类型的类图

 
二、数据结构
几个实现了Writable接口的数据结构如下

主要有4种Writable型数据结构:分别是ArrayWritable,TwoDArrayWritable,MapWritable和SortedMapWritable
1、  ArrayWritable
看到ArrayWritable的构造函数有个形式如下:
private Class<? extends Writable> valueClass;
private Writable[] values;
  public ArrayWritable(Class<? extends Writable> valueClass) {
    if (valueClass == null) {
      throw new IllegalArgumentException("null valueClass");
    }  
    this.valueClass = valueClass;
  }
 
  public ArrayWritable(Class<? extends Writable> valueClass, Writable[] values) {
    this(valueClass);
    this.values = values;
  }
 
  public ArrayWritable(String[] strings) {
    this(UTF8.class, new Writable[strings.length]);
    for (int i = 0; i < strings.length; i++) {
      values[i] = new UTF8(strings[i]);
    }
    第三个构造函数中,传入的是一个字符串数组,则自动将其进行打包,使用UTF8这个类进行封装,该类实现了WritableComparable 接口。这样,能够方便的处理字符串数组。因此,除了String类型的数据,该数据结构不能够存储其他未实现Writable接口的数据。
2、  TwoDArrayWritable 是二维数组。实现不复杂,主要还是实现了Writable接口,
 
public Object toArray() {
    int dimensions[] = {values.length, 0};
    Object result = Array.newInstance(valueClass, dimensions);
    for (int i = 0; i < values.length; i++) {
      Object resultRow = Array.newInstance(valueClass, values[i].length);
      Array.set(result, i, resultRow);
      for (int j = 0; j < values[i].length; j++) {
        Array.set(resultRow, j, values[i][j]);
      }
    }
    return result;
  }
该toArray方法将二维数组以对象的形式返回,使用了自带的Array里面的newInstance和set方法,而不是传统的
Array arr = newArrayList();
Arr.add(newArrayList())
的方式。自带的Array这种方式能够创建任意维度的数组(<255),并且似乎

补充:综合编程 , 其他综合 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,