mapreduce 利用InverseMapper.class对key,value进行 交换实现词频排序
本程序的功能是对输入的数据进行词频统计然后再根据词频大小对出现的单词进行排列
1.实现的map类
这个类实现 Mapper 接口中的 map 方法,输入参数中的 value 是文本文件中的一行,利用StringTokenizer 将这个字符串拆成单词,然后将输出结果org.apache.hadoop.mapred.OutputCollector 中。OutputCollector 由 Hadoop框架提供, 负责收集 Mapper 和 Reducer 的输出数据,实现 map 函数和 reduce
函数时,只需要简单地将其输出的对往 OutputCollector 中一丢即可,剩余的事框架自会帮你处理好。
代码如下:
import java.io.IOException;
import java.util.StringTokenizer;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
public class classCountWordMaper extends Mapper<LongWritable, Text, Text, IntWritable> {
@Override
protected void map(LongWritable key,Text value,
Context context)
throws IOException,InterruptedException {
String text=value.toString();
StringTokenizer tokens=new StringTokenizer(text);
while(tokens.hasMoreTokens()){
String str=tokens.nextToken();
context.write(new Text(str), new IntWritable(1));
}
}
}
2.实现reduce类
这个类实现 Reducer 接口中的 reduce 方法, 输入参数中的 key, values 是由 Map
任务输出的中间结果,values 是一个 Iterator, 遍历这个 Iterator, 就可以得到属于同一个 key 的所有 value.
此处,key 是一个单词,value 是词频。只需要将所有的 value 相加,就可以得到这个单词的总的出现次数。
代码如下:
import java.io.IOException;
import java.util.Iterator;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
public class classCountWordReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
@Override
protected void reduce(Text key,Iterable<IntWritable> values, Context context)
throws IOException,InterruptedException {
int total=0;
Iterator<IntWritable>it=values.iterator();
while(it.hasNext()){
it.next();
++total;
}
context.write(key, new IntWritable(total));
}
}
3.由于reduce默认的是对key进行升序排列,而我们要实现降序排列,所以就要实现一个类
import org.apache.hadoop.io.IntWritable.Comparator;
import org.apache.hadoop.io.WritableComparable;
public class IntWritableDecreasingComparator extends Comparator {
public int compare(WritableComparable a,WritableComparable b){
return -super.compare(a, b);
}
@Override
public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
// TODO Auto-generated method stub
return -super.compare(b1, s1, l1, b2, s2, l2);
}
}
4.主程序:
定义了2个job,第一个job是对数据进行词频统计,并且把结果输入到临时文件当中,然后利用mapreduce的管道功能,再递交第二个job,将第一个job
的输出作为第二个排序任务的输入,注意要文件读入的格式一定要符合数据类型。第二个job通过调用系统给出的jobsort.setMapperClass(InverseMapper.class);
作为map函数,reduce利用默认IdentityReducer讲最后map输出的结果输出到输出文件当中。
代码如下:
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.Text;
//import org.apache.hadoop.mapred.TextInputFormat;
//import org.apache.hadoop.mapred.KeyValueTextInputFormat;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.map.InverseMapper;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.TextOutputFormat;
import org.apache.hadoop.mapreduce.lib.input.SequenceFileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
public class classCountWord {
public static void main(String[] args)throws IOException, InterruptedException, ClassNotFoundException {
Job job=new Job();
job.setJarByClass(classCountWord.class);
Path tempath=new Path(args[1]);
Configuration conf=new Configuration();
FileInputFormat.addInputPath(job,new Path(args[0]));
FileSystem.get(conf).delete(tempath);
FileOutputFormat.setOutputPath(job,tempath);
job.setMapperClass(classCountWordMaper.class);
job.setReducerClass(classCountWordReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
job.setOutputFormatClass(SequenceFileOutputFormat.class);
job.waitForCompletion(false);
System.out.println("开始第二个任务");
Job jobsort=new Job();
FileInputFormat.addInputPath(jobsort, tempath);
jobsort.setOutputKeyClass(IntWritable.class);
jobsort.setOutputValueClass(Text.class);
jobsort.setInputFormatClass(SequenceFileInputFormat.class);
jobsort.setMapperClass(InverseMapper.class);
jobsort.setNumReduceTasks(1);
Path result=new Path("/home/hadoop/result");
FileSystem.get(conf).delete(result);
FileOutputFormat.setOutputPath(jobsort, result);
jobsort.setSortComparatorClass(IntWritableDecreasingComparator.class);
jobsort.waitForCompletion(false);
}
}
注意:写程序的时候遇到的问题:
java.io.IOException: Type mismatch in key from map: expected org.apache.hadoop.io.IntWritable, recieved org.apache.hadoop.io.Text
问题在于第二个任务读取第一个任务的输出结果的格式不对。因为默认的输出是TextOutputFormat,但是import org.apache.hadoop.mapreduce.lib.input包中没有TextInputFormat,所以出现格式不匹配的错误,后来我把第一个任务的输出格式和第二个任务读取数据的格式都设置成
job.setOutputFormatClass(SequenceFileOutputFormat.class);
jobsort.setInputFormatClass(SequenceFileInputFormat.class);
后来程序就通了,希望大家少走弯路,能够对大家有点帮助,有问题欢迎指正~~~