关于mapreduce中的倒排索引例题以及StringBuilder的注意事项
今天学习了mapreduce中的倒排索引,遇到一个求微博共同关注的题目:
周杰伦:科比,蔡依林,林书豪,林俊杰
姚明:林书豪,詹姆斯,哈登,蔡依林
蔡依林:林俊杰,周杰伦,大S,林书豪
林书豪:詹姆斯,科比,保罗,艾弗森
上述数据格式为:
账号:关注的人
需求:
现在找出哪些人 两两之间有共同关注的人,以及共同关注的人都有谁?
想得到这样的一个结果:
(周杰伦-蔡依林,{林俊杰,林书豪})
**
解题思路:
**
1.根据mapreduce中shuffle阶段根据相同key分组的思想,通过第一个mapreduce求出被关注人的数组
2.再通过第二个mapreduce,对数据进行处理,最终得出我们想要的结果
做题的过程中,思路是比较清晰的,附上代码:
第一个map:
public class ConcernMapper1 extends Mapper<LongWritable, Text, Text, Text> {
Text k = new Text();
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//1、利用mapreduce会对相同key进行分组的特点,将被 <被关注者,关注者>输出到reduce端
String[] line = value.toString().split(":");
String concernPeoson = line[0];//关注者
String[] names = line[1].split(",");//关注的人
for (String str : names) {
k.set(str);
/*
* 输出数据:
* 林书豪 姚明
* 林书豪 蔡依林
* 林书豪 周杰伦
*
*
* 经过shuffle阶段到reduce时,数据变成了
*
* 林书豪 {姚明 蔡依林 周杰伦}
*
*
*
*
* */
context.write(k, new Text(concernPeoson));
}
}
}
第一个reduce:
public class ConcerReduce1 extends Reducer<Text, Text, Text, Text> {
Text k = new Text();
StringBuilder sb = new StringBuilder();
@Override
protected void reduce(Text key, Iterable<Text> values, Context context)
throws IOException, InterruptedException {
for (Text concerned : values) {
sb.append(concerned.toString()).append(",");
}
k.set(sb.toString());
context.write(key, k);
}
}
第二个map:
public class ConcernMapper2 extends Mapper<LongWritable,Text,Text,Text> {
String name = "";
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String[] split = value.toString().split("\t");
String concernedPeoson = split[0];//被关注的人
//对关注人的数组进行排序,防止出现 周杰伦-姚明 与 姚明-周杰伦,被reduce认为时不同的key,从而但不到需求
String[] concernPeople = split[1].split(",");//关注者
Arrays.sort(concernPeople);
//将排序后的[周杰伦,姚明,蔡依林]数组 [蔡依林,姚明,周杰伦] 以 周杰伦-姚明 周杰伦-蔡依林的形式输出
for (int i = 0;i<concernPeople.length-1;i++){
for (int j = 1 ;j<concernPeople.length;j++) {
name =concernPeople[i] + "-" + concernPeople[j];
context.write(new Text(name), new Text(concernedPeoson));
}
}
}
}
第二个reduce:
public class ConcernReduce2 extends Reducer<Text,Text,Text,Text> {
StringBuilder stringBuilder = new StringBuilder();
@Override
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
for (Text concerned : values){
stringBuilder.append(concerned.toString()).append(" ");
}
context.write(key,new Text(stringBuilder.toString()) );
}
}
但是碰到了一个没有注意到的问题,导致输出的结果不符合要求,以下是运行第一个mapreduceDriver后的结果:
运行第二个driver后的结果:
输出的结果差强人意,十分的杂乱
回头看了下代码,最终发现问题是将StringBuilder sb = new StringBuilder();写在reduce方法外,导致sb这个实例一致存在,且不断添加字符。
解决办法: 将StringBuilder sb = new StringBuilder();写在reduce方法内部
输出正常,问题解决