在MapReduce编程模型中key通常是用来排序和划分的。排序是指按照key的大小顺序将 <k,v> 键值对排序,划分是指按照key的hashcode值将 <k,v>划分到指定的Reducer节点上。
MapReduce中的key类型必须实现WritableComparable接口,为了方便用户使用,Hadoop提供有一些内置的key类型。常见的key类型有 IntWritable 、LongWritable 、Text、FloatWritable等。但有时我们还需使用自己定义的数据类型作为key。
下面小编就用数据表求交集的例子来介绍自定义数据类型怎么作为key。
有如下两个数据表,数据关系属于同一模型,字段有 id、name 、age 、grade。数据表table1和table2中的内容如下所示。
求交集的目的是输出两个表中相同的记录。如上table1和table2所示,应该输出id为1、2的记录。
求交集的思想为在Map阶段对每一条记录r输出 <r,1>,然后在Reduce阶段汇总计数,将计数为2的记录r输出即可。
我们用一个Stu类来存储一条记录,并将Stu类作为key使用,Stu类要实现WritableComparable接口,要注意一下几点:
必须有一个无参的构造函数。
必须重写WritableComparable接口的hashCode()方法和equals()方法以及compareTo()方法。
Stu类的代码如下:
package Eg1;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import org.apache.hadoop.io.WritableComparable;
/**
* Stu类设计
* @author liuchen
*/
public class Stu implements WritableComparable<Stu>
{
private int id;
private String name;
private int age;
private int grade;
//无参的构造函数必不可少
public Stu(){
}
public Stu(int a, String b, int c, int d){
this.id = a;
this.name = b;
this.age = c;
this.grade = d;
}
public void readFields(DataInput in) throws IOException {
this.id = in.readInt();
this.name = in.readUTF();
this.age = in.readInt();
this.grade = in.readInt();
}
public void write(DataOutput out) throws IOException {
out.writeInt(id);
out.writeUTF(name);
out.writeInt(age);
out.writeInt(grade);
}
//按照id降序排列
public int compareTo(Stu o) {
return this.id >= o.id ? -1 : 1 ;
}
public int hashCode() {
return this.id + this.name.hashCode() + this.age + this.grade;
}
public boolean equals(Object obj) {
if (!(obj instanceof Stu)) {
return false;
}else{
Stu r = (Stu) obj;
if (this.id == r.id && this.name.equals(r.name) && this.age == r.age && this.grade == r.grade){
return true;
}else{
return false;
}
}
}
public String toString() {
return Integer.toString(id) + "\t" + name + "\t" + Integer.toString(age) + "\t" + Integer.toString(grade);
}
}
Map阶段的map()函数如下:
protected void map(LongWritable key, Text value,Context context)throws IOException, InterruptedException {
final IntWritable one = new IntWritable(1);
String[] arr = value.toString().split("\t");
Stu stu = new Stu(Integer.parseInt(arr[0]),arr[1],Integer.parseInt(arr[2]),Integer.parseInt(arr[3]));
context.write(stu,one);
}
Reduce阶段的reduce()函数如下:
protected void reduce(Stu arg0, Iterable<IntWritable> arg1,Context arg2)throws IOException, InterruptedException {
int sum = 0;
for(IntWritable val : arg1){
sum += val.get();
}
if(sum == 2){
arg2.write(arg0,NullWritable.get());
}
}