1.在Mapper类中,文件的每一行都会调用重写的方法, 但是方法外面的只会执行一次, 所以可以把获取配置对象和泛型对象写在重写方法的外面, 节约内存
2.在Reducer类中,从Mapper类来的每一个K-V都会调用重写的方法, 但是方法外面的只会执行一次.
PS: 不是每次都执行一次Mapper类和Reducer类
举例说明 :
(例子中的注释会说的很明白!)
求学生的平均成绩:
名字 语文 数学 英语
lh 92 68 70
zyt 94 88 75
ls 90 78 78
hgw 90 70 56
yxx 80 88 73
hz 90 98 70
xyd 68 88 73
hj 90 58 70
cs 50 58 11
注: 我这里是以制表符(tab)划分的,建议各位如果要试验的话不要复制我的数据,可能分隔符会有问题, 因为分隔符出错了可不要怪我哦
Mapper类:
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
public class ScoreMapper extends Mapper<LongWritable, Text, Text, Text>{
Configuration conf = new Configuration();//获取配置信息
Text keytext = new Text();
Text valuetext = new Text();
@Override
protected void map(LongWritable key, Text value,Context context)
throws IOException, InterruptedException {
//我的数据是以制表符分割,所以split中的正则表达式是"\t"
String[] split = value.toString().split("\t");
//切割后的数组第一个是名字
keytext.set(split[0]);
for(int i = 1; i<split.length; i++){
valuetext.set(split[i]);
//输出的K-V,key是名字,value是成绩的集合,当然还有第一行的各学科名字
context.write(keytext, valuetext);
}
}
}
Reducer类:
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
public class ScoreReducer extends Reducer<Text, Text, Text, Text>{
Configuration conf = new Configuration();
@Override
protected void reduce(Text key, Iterable<Text> value,Context context)
throws IOException, InterruptedException {
//不能写在这个方法的上面,因为每次都是调用这么方法而不是从头执行这个Reducer类
//如果写在外面的话,因为Double是引用类型,下一次执行这个方法时sum就不是从零开始,求的平均值就是之前所有成绩的
//当然,如果写在外面的话,也可以在最后context.write();的下面将sum置零,即sum=0.0也可解决
Double sum = 0.0;
if(key.toString().equals("名字")){
context.write(key, new Text("平均成绩"));
}else{
for (Text text : value) {
Double valueOf = Double.valueOf(text.toString());
sum += valueOf;
}
Double avg = sum / 3.0;
//按四舍五入格式化avg,只保留两位小数,第一个参数是格式化控制符,第二个参数是要格式化的字符串
//格式化控制符要和格式化字符串一一对应
String format = String.format("%.2f", avg);
context.write(key, new Text(format));
}
}
}
测试类:
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;
public class ScoreTest {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
//接收参数,要传参
String[] otherargs = new GenericOptionsParser(conf, args).getRemainingArgs();
if(otherargs.length != 2){
System.out.println("Usage: in out");
System.exit(2);
}
Job job = Job.getInstance(conf, "score-avg");
job.setJarByClass(ScoreTest.class);
job.setMapperClass(ScoreMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
job.setReducerClass(ScoreReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
//判断hdfs服务器上是不是已经存在输出的文件夹,如果存在就进行删除
//这样就不用每次进行手动删除了
//如果不删除就输出到已经存在的文件夹下回报错的
Path path = new Path(otherargs[1]);
FileSystem fileSystem = path.getFileSystem(conf);
if(fileSystem.exists(path)){
fileSystem.delete(path, true);
}
FileInputFormat.setInputPaths(job, new Path(otherargs[0]));
FileOutputFormat.setOutputPath(job, new Path(otherargs[1]));
boolean waitForCompletion = job.waitForCompletion(true);
System.exit(waitForCompletion?0:1);
}
}
OK!!!
上面代码sum写在方法里面就可以计算每个人的平均成绩, 写在外面就是该同学的平均值是他前面所有人的成绩的平均值, 能到好几百, 这平均值要报表啊有木有.
就到这吧, 希望大家能在Hadoop的学习之路上一起进步!