如何写一个属于自己的数据类型(Writable)
疑问一
IntWritable, LongWritable, Text和Java的int, long, String有什么区别?
答:数据在集群中传输时,xxWritable转化成字节流(byte[])进行传输,具体方法为接口Writable的write(DataOutput out);解析还原时,使用接口Writable的readFields(DataInput in)进行还原;排序比较时,使用接口Comparable的compareTo方法进行比较。
写属于自己的IntWritable
PS:以下代码参照IntWritable源码
public class MyInt implements WritableComparable<MyInt> {
// 就是对这个value的封装
private int value;
// 构造方法
public MyInt() {}
public MyInt(int i) { this.value = i; }
// getter and setter
public int get() { return this.value; }
public void set(int i) { this.value = i; }
/
*******************************************************/
/* readFields和write可以自己自定义,只要能正确还原value即可 */
/*******************************************************/
// 解析字节流,即还原数据
@Override
public void readFields(DataInput in) throws IOException {
this.value = in.readInt();
}
// 把value转化成字节流
@Override
public void write(DataOutput out) throws IOException {
out.writeInt(this.value);
}
/*******************************************************/
/* 修改compareTo方法即可自定义MyInt类型的排序方式 */
/*******************************************************/
@Override
public int compareTo(MyInt o) {
// 按照从小到大排序
if (this.value != o.value) {
return this.value < o.value ? -1 : 1;
} else {
return 0;
}
}
/*******************************************************/
/* equals, hashCode, toString可选,最好实现。 */
/*******************************************************/
@Override
public int hashCode() {
return this.value;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof MyInt)) {
return false;
}
return ((MyInt) o).value == this.value;
}
@Override
public String toString() {
return Integer.toString(this.value);
}
}
/*******************************************************/
/* 不使用MyInt重写的compareTo方法, */
/* 而使用下面注册的WritableComparator的compare方法。 */
/* 下面一部分讲如何写WritableComparator类。 */
/* 注意:下面的代码不能写在MyInt类外,会不起作用。 */
/*******************************************************/
static {
WritableComparator.define(MyInt.class, new IntComparator());
}
疑问二
如果不想自己写Writable,就想用现成提供的(用IntWritable举例),但是IntWritable默认的是从小到大的排序,我想从大到小或者第一位先排序再按大小排序等等(后者排序为:1,11,123,2,21,231)。该如何操作?
答:和Java原生的Comparator接口类似,Hadoop用的是RawComparator(也是继承Comparator),默认实现类是:WritableComparator。
所以,实现RawComparator接口或者继承WritableComparator即可。
通常使用继承WritableComparator的方式,因为RawComparator是对字节流的比较,需要熟悉了解字节流的比较方法;而WritableComparator内涵许多对字节流的处理方法,可以用于转化字节流。
写属于自己的IntComparator
下面使用继承WritableComparator的方式来写自己的IntComparator。
(实现RawComparator的方式参考WritableComparator的内部实现)
/**
* WritableComparator主要用来比较的方法有三个:
* 方法1,compare(byte[],int,int,byte[],int,int);
* 方法2,compare(WritableComparable,WritableComparable);
* 方法3,compare(Object,Object);
*
* 关系:
* 1,方法3实际调用方法2,所以方法2和方法3其实一样;
* 2,方法1的逻辑是把字节流转化成指定的Writable,这里是MyInt.class
* 3,实际使用中,只会调用方法1,而方法1还原后,调用方法2。
*
*/
public class IntComparator extends WritableComparator {
/**
* 最后一个参数必须是true,参数名为:createInstances,
* 意思是:是否创建方法1中用到的还原目标对象实例。
* 这里不重写方法1,所以必须为true;默认为false,重写方法1则可以为false。
*/
public IntComparator() {
super(MyInt.class, true);
}
@Override
public int compare(WritableComparable a, WritableComparable b) {
int thisValue = ((MyInt) a).get();
int thatValue = ((MyInt) b).get();
// 从大到小
if (thisValue != thatValue) {
return thisValue > thatValue ? -1 : 1;
} else {
return 0;
}
}
}
写完WritableComparator后,需要在Job配置一下
someMethod() {
Job job = Job.getInstance();
(省略一些代码)
job.setSortComparatorClass(IntComparator.class);
(省略一些代码)
}
key排序优先级
三种key排序方法,优先级从高到低:
- Job.setSortComparatorClass();
- WritableComparator.define();
- Comparable中的compareTo();