1.通过理论联系实际,引导学生依据所掌握的Java语言程序设计和分布式编程框架知识,在理解MapReduce运行机制的基础上,研究分析和解决实际问题方案
2.掌握自主学习的方法,加深相关知识点的理解,提高分析问题的能力以及编码、调试的能力,促进课程目标的达成。
1.单词统计:利用倒排索引实现每个单词在不同文件中出现的次数
2.手机上网流量统计:按照手机号码分别统计上网的上下行流量,并按照总流量从大到小排序
3.社交粉丝数据分析:根据已存在的qq好友列表数据,求出哪些人两两之间有共同好友,及他俩的共同好友都有谁
1.单词统计
设计思路:
①因为需要计算每个单词在不同文件中出现的次数,因此首先将常规mapper的输出格式 <单词,1> 变为 <单词:文件名,1>,即单词和文件名作为key,1作为value;
②然后使用combine先将数据<单词:文件名,1,1,1…>中的values进行合并(累加),然后再将数据形式变为以单词作key,文件名和出现次数作value,即<单词, 文件名-出现的总次数(2/3/4….)>;
③然后使用Reducer再进行汇总,得出每个单词在每个文件中出现的次数。
Map端程序:(给出map方法即可)
String line = ivalue.toString(); //取出一行
String[] words = line.split(" "); //切分line.split(" ")
FileSplit fileSplit = (FileSplit) context.getInputSplit(); //取出当前处理切片所在的文件名
String filename = fileSplit.getPath().getName();
for (String word : words) { //输出<单词:文件名,1>
context.write(new Text(word+":"+filename),new IntWritable(1)); //将单词和文件名作为key,1作为value
}
Combiner端程序:
int sum = 0; // 首先求和,将value的值累加
for (IntWritable val : values) {
sum+=val.get();
}
//处理之后数据变为<单词:文件名,出现的总次数(例如3)>
//将key中的单词和文件名使用“:”进行拆分
String[] key = _key.toString().split(":");
context.write(new Text(key[0]), new Text(key[1]+"-"+sum)); //然后将单词作为key,文件名和出现的次数作为value
Reduce程序:(给出reduce方法即可)
String value = "";
for (Text val : values) {
value += val.toString()+";"; //将values的值通过“;”连接
}
context.write(_key, new Text(value)); //最后将<单词,文件名1-2;文件名2-3;文件名3...>输出
Driver程序:(给出map输出和reduce输出设置即可)
job.setMapOutputKeyClass(Text.class); //mapper的输出
job.setMapOutputValueClass(Text.class);
job.setOutputKeyClass(Text.class); //Reducer的输出
job.setOutputValueClass(Text.class);
2.手机上网流量统计
设计思路:
①先按手机号统计上网的上下行流量,即按手机号的升序统计出结果;
②在①的基础上,将手机上网流量(FlowBean)作为key,然后自定义两个FlowBean对象的比较方法,让其按照从大到小的顺序排列
Map端程序:(给出map方法即可)
①的Map端程序:
String line = ivalue.toString(); //取出手机号和上行、下行流量
String[] strings = line.split("\t"); //切分字段
String phoneNu = strings[1]; //手机号
long upFlow = Long.parseLong(strings[strings.length-3]); //上行流量
long downFlow = Long.parseLong(strings[strings.length-2]); //下行流量
context.write(new Text(phoneNu), new FlowBean(upFlow,downFlow));
②的Map端程序:
String line = ivalue.toString(); // 取出手机号和上行、下行流量
String[] strings = line.split("\t"); // 切分字段
String phoneNu = strings[0]; // 手机号
long upFlow = Long.parseLong(strings[1]); // 上行流量
long downFlow = Long.parseLong(strings[2]); // 下行流量
//将总的流量作为key,手机号作为value,让其按总流量进行排序
context.write(new FlowBean(upFlow, downFlow),new Text(phoneNu));
Reduce端程序:(给出reduce方法即可)
①的Reduce端程序:
long sum_upFlow = 0;
long sum_downFlow = 0;
//将一个手机号中的所有上行、下行流量累加,即对values中的数据求和
for (FlowBean val : values) {
sum_upFlow += val.getUpFlow();
sum_downFlow += val.getDownFlow();
}
context.write(_key, new FlowBean(sum_upFlow, sum_downFlow));
②的Reduce端程序:
for (Text val : values) { //遍历相同上网流量的手机号列表
//将输出结果仍然变成手机号为key,流量为value
for (Text phone : values) {
context.write(new Text(phone), _key);
}
}
Driver程序:(给出map输出和reduce输出设置即可)
①的Driver端程序:
job.setMapOutputKeyClass(Text.class); //mapper的输出
job.setMapOutputValueClass(FlowBean.class);
job.setOutputKeyClass(Text.class); //reduce的输出
job.setOutputValueClass(FlowBean.class);
②的Driver端程序:
job.setMapOutputKeyClass(FlowBean.class); //mapper的输出结果
job.setMapOutputValueClass(Text.class);
job.setOutputKeyClass(Text.class); // reduce的输出结果
job.setOutputValueClass(FlowBean.class);
FlowBean程序:(两个对象进行比较大小)
@Override //定义两个对象进行比较大小
public int compareTo(FlowBean o) {
return this.sumFlow > o.getSumFlow()?-1:1;
}
3.社交粉丝数据分析
设计思路:
阶段一:将数据整理成<好友1,用户1,用户2,用户3…..>
①使用mapper先将文件使用” : ”进行分割,得到的好友再用”,”分割,接着将好友作为key,该用户作为value,有点类似于第1题,注意好友有多个,因此使用for循环将<好友1,用户1>作为输出写入context中;
②Reducer接收到数据后将同一好友的value值使用”,”进行合并,利用字符串处理,最后输出数据为<好友1,用户1,用户2,用户3…..>…..
阶段二:在阶段一的基础上将数据整理成<用户1-用户2,好友1,好友2,…..>
①使用mapper将数据中的key和value倒过来,并且注意将所有的用户利用for循环进行两两组合排列(用户1-用户2)作为输出的key,而原数据的key作为输出的value,即<用户1-用户2,好友1>;
②Reducer接收到数据后将同一组用户的value值使用”,”进行合并,同样利用字符串处理,最后输出数据为<用户1-用户2,好友1,好友2,…..>…..
Map端程序:(给出map方法即可)
阶段一的Map端程序:
//取出一行数据,并将其分割
String[] strings = ivalue.toString().split(":");
String[] frineds = strings[1].split(",");//该用户的所有好友
for (String string : frineds) {
//将好友作为key,该用户作为value输出
context.write(new Text(string), new Text(strings[0]));
}
阶段二的Map端程序:
// 将数据以空格分割
String[] strings = ivalue.toString().split(" ");
String[] users = strings[1].split(";"); // 拥有该好友的所有用户
for (int i = 0; i < users.length; i++) {
//将所有用户进行两两组合排列
for (int j = i+1; j < users.length; j++) {
// 将两个用户组合作为key,该好友作为value输出
context.write(new Text(users[i].toString()+"-"+users[j].toString()), new Text(strings[0]));
}
}
Reduce端程序:(给出reduce方法即可)
阶段一的Reduce端程序:
String value = "";
// 将同一好友的value值进行合并
for (Text val : values) {
value += val.toString()+";";
}
context.write(_key, new Text(value));
阶段二的Reduce端程序:
// 将接收的数据中同一组用户的value值进行合并
String value = "";
for (Text val : values) {
value += val.toString()+";";
}
context.write(_key, new Text(value));
Driver程序:(给出map输出和reduce输出设置即可)
阶段一的Driver程序:
//mapper的输出
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
// reducer的输出
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
阶段二的Driver程序:
//mapper的输出
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
//reducer的输出
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);