为了支持以上这些特性,Hadoop引入org.apache.hadoop.io.Writable接口,作为所有可序列化对象必须实现的接口,其类图如图3-2所示。
Writable机制紧凑、快速(但不容易扩展到Java以外的语言,如C、Python等)。和java.io.Serializable不同,Writable接口不是一个说明性接口,它包含两个方法:
- public interface Writable {
- /**
- * 输出(序列化)对象到流中
- * @param out DataOuput流,序列化的结果保存在流中
- * @throws IOException
- */
- void write(DataOutput out) throws IOException;
- /**
- * 从流中读取(反序列化)对象
- * 为了效率,请尽可能复用现有的对象
- * @param in DataInput流,从该流中读取数据
- * @throws IOException
- */
- void readFields(DataInput in) throws IOException;
- }
Writable.write()方法用于将对象状态写入二进制的DataOutput中,反序列化的过程由readFields()从DataInput流中读取状态完成。下面是一个例子:
- public class Block implements Writable, Comparable<Block>, Serializable {
- ……
- private long blockId;
- private long numBytes;
- private long generationStamp;
- ……
- public void write(DataOutput out) throws IOException {
- out.writeLong(blockId);
- out.writeLong(numBytes);
- out.writeLong(generationStamp);
- }
- public void readFields(DataInput in) throws IOException {
- this.blockId = in.readLong();
- this.numBytes = in.readLong();
- this.generationStamp = in.readLong();
- if (numBytes < 0) {
- throw new IOException("Unexpected block size: " + numBytes);
- }
- }
- ……
- }
这个例子使用的是前面分析Java序列化机制的Block类,Block实现了Writable接口,即需要实现write()方法和readFields()方法,这两个方法的实现都很简单:Block有三个成员变量,write()方法简单地把这三个变量写入流中,而readFields()则从流中依次读入这些数据,并做必要的检查。
Hadoop序列化机制中还包括另外几个重要接口:WritableComparable、RawComparator和WritableComparator。
WritableComparable,顾名思义,它提供类型比较的能力,这对MapReduce至关重要。该接口继承自Writable接口和Comparable接口,其中Comparable用于进行类型比较。ByteWritable、IntWritable、DoubleWritable等Java基本类型对应的Writable类型,都继承自WritableComparable。