Writable类和WritableComparable类 序列化和反序列化
Writable :对Java基本类型提供封装(short和char除外),使其可以实现序列化和反序列。所有的封装包包含get() 和 set() 方法用于读取或者设置封装的值。在Hadoop中定义一个结构化对象都要实现,使得该结构化对象可以序列化为字节流,字节流也可以反序列化为结构化对象。
WritableComparable :WriteCompareable接口是Wirtable接口的二次封装,提供了compareTo(T o)方法,用于序列化对象的比较的比较MapReduce中所有的key值类型都必须实现这个接口,下面是io包简单的类图关系。 字节流、字符流: 字符和字节详解、Java中字节串和字符串相互转换 Java IO 字节流、字符流详解
1. Writable类
在Hadoop中,Writable接口定义了两个方法: Write() : 用于将其状态写入二进制格式的DataOutput 流。 read() : 用于从二进制格式的DataInput 流读取其状态。
package org.apache.hadoop.io;
import java.io.DataOutput;
import java.io.DataInput;
import java.io.IOException;
public interface Writable {
void write(DataOutput out) throws IOException;
void readFields(DataInput in) throws IOException;
}
-
将来自in (输入流)中对象的字段反序列化。就效率而言,这些实现(implements)应该尝试在可能存在的对象下重用存储。 -
基于DataInput 和DataOutput ,实现一个简单、有效、有序列化协议的序列化对象。 -
Hadoop Map-REDUCE框架中的任何key或value类型都必须实现这些接口。 -
典型的实现是实现静态的read(DataInput) 方法,这个方法构造了一个新的实例;同时调用readFields(DataInput) 方法并且返回一个实例。
Implementations typlically
public class MyWritable implements Writable {
private int counter;
private long timestamp;
public void write(DataOutput out) throws IOException {
out.writeInt(counter);
out.writeLong(timestamp);
}
public void readFields(DataInput in) throws IOException {
counter = in.readInt();
timestamp = in.readLong();
}
public static MyWritable read(DataInput in) throws IOException {
MyWritable w = new MyWritable();
w.readFields(in);
return w;
}
}
DataOutput和DataInput DataOutput 接口用于将数据从任意 Java 基本类型转换为一系列字节,并将这些字节写入二进制流。同时还提供了一个将 String 转换成 UTF-8 修改版格式并写入所得到的系列字节的工具。
DataInput 接口提供用于读取二进制流字节并重建为任何java原始数据类型,也提供了一个转换为UTF-8编码的字符串类型方法。
2. WritableComparable类
WritableComparable 继承了Writable接口和Comparable接口,由于WritableComparable中没有定义要实现的方法,所以继承这个接口,要实现的方法都是Writable接口和Comparable接口中的方法。
Writable接口 实现write和readFields方法,write是要对out对象进行序列化操作,readFields是对in对象进行反序列化操作。
public interface Writable {
void write(DataOutput out) throws IOException;
void readFields(DataInput in) throws IOException;
}
Comparable接口 将调用该方法的对象和传入参数对象进行比较
public int compareTo(T o);
返回值有三种: 负整数,此时调用该方法的对象小于指定对象; 0,二者相等; 正整数,调用该方法的对象大于指定对象。
针对key自定义排序示例
public class MyWritableComparable implements WritableComparable<MyWritableComparable> {
private int counter;
private long timestamp;
public void write(DataOutput out) throws IOException {
out.writeInt(counter);
out.writeLong(timestamp);
}
public void readFields(DataInput in) throws IOException {
counter = in.readInt();
timestamp = in.readLong();
}
public int compareTo(MyWritableComparable o) {
int thisValue = this.value;
int thatValue = o.value;
return (thisValue < thatValue ? -1 : (thisValue==thatValue ? 0 : 1));
}
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + counter;
result = prime * result + (int) (timestamp ^ (timestamp >>> 32));
return result
}
}
注意,实现序列化和反序列化方法时,写入和读取属性的顺序一定要相同!!!实现完成之后,这个实现类就可以作为Map阶段输入或者输出数据的key类型,进行输出。在compareTo方法中实现自定义排序。
3. Java基本类型的Writable类
IntWritable 和 VIntWritable 这两个的区别是 IntWriable 是固定的4个字节,而 VintWritable 是1~5个字节,是可以变化的。相比与定长格式,变长格式有什么优点:
(1)定长格式适合对整个值域空间中分布均匀的数值进行编码,大多数数值变量的分布都不均匀,而采用变长格式一般更节省空间。
(2)变长格式的 VIntWritable 和 VLlongWritable 可以转换,因为他们的编码实际上一致。选择变长格式,更有增长空间。
参考资料: Hadoop中Writable类
Hadoop源码解析之Writable类
接口 DataOutput
(一)java.io.DataInput接口及源码详解
自定义排序WritableComparable
|