花了2个星期刷完了尚硅谷hadoop课程,虽然是第二次学习hadoop,但是收获也非常大,今天分享一下我对mapreduce最后一个题目-寻找博主共同好友的解题思路
一.需求
以下是博主的好友列表数据,冒号前是一个用户,冒号后是该用户的所有好友,数据中的好友关系是单向的,共有14个博主
A:B,C,D,F,E,O
B:A,C,E,K
C:F,A,D,I
D:A,E,F,L
E:B,C,D,M,L
F:A,B,C,D,E,O,M
G:A,C,D,E,F
H:A,C,D,E,O
I:A,O
J:B,O
K:A,C,D
L:D,E,F
M:E,F,G
O:A,H,I,J
最终输出格式
A和B的共同好友有:C, E
A和C的共同好友有:F, D
A->B [C, E]
A->C [F, D]
.....
二.思路分析
1.每个博主之间共有 n*(n-1) / 2 种组合方式,即14*13 / 2 = 91 种组合方式
2.使用HashMap存储每个博主及对应好友信息,博主作为 key,好友作为 value
3.使用两层for循环遍历每个博主,外层博主和内层博主进行组合,其中加以判断过滤重复的组合
4.使用ArrayList存储外层循环遍历过的博主,内层循环遍历博主时判断该博主是否存在于这个 ArrayList,如果存在则 continue
三.代码演示
说明:
1.使用KeyValueTextInputFormat作为输入格式
2.仅使用Mapper即可完成该需求
public class FindCommonFriends {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//1.获取job对象
Configuration conf = new Configuration();
//设置分隔符
conf.set(KeyValueLineRecordReader.KEY_VALUE_SEPERATOR, ":");
Job job = Job.getInstance(conf);
//2.设置jar路径
job.setJarByClass(FindCommonFriends.class);
//3.关联mapper
job.setMapperClass(FrientMapper.class);
//4.设置mapper输出key和value类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
//5.设置最终输出的key和value类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
//设置输入格式
job.setInputFormatClass(KeyValueTextInputFormat.class);
//6.设置输入输出路径
FileInputFormat.addInputPath(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
//7.提交job
boolean b = job.waitForCompletion(true);
System.exit(b ? 0 : 1);
}
}
class FrientMapper extends Mapper<Text, Text, Text, Text> {
HashMap<String, String> hashMap = new HashMap<>();
@Override
protected void map(Text key, Text value, Context context) throws IOException, InterruptedException {
//将key value 对存入 hashmap
hashMap.put(key.toString(), value.toString());
}
@Override
protected void cleanup(Context context) throws IOException, InterruptedException {
System.out.println(hashMap);
//1 遍历hashmap
Set<Map.Entry<String, String>> entries = hashMap.entrySet();
//2 定义一个集合,用于添加标记
ArrayList<String> flags = new ArrayList<>();
//第一层遍历
for (Map.Entry<String, String> entry1 : entries) {
//第二层遍历
for (Map.Entry<String, String> entry2 : entries) {
//3 判断里层 key 在外层循环中是否遍历
if (flags.contains(entry2.getKey())) {
continue;
}
//4 判断外层 key 和里层 key 是否相同
if (entry2.getKey().equals(entry1.getKey())) {
continue;
}
//5 定义一个集合,用于存放共同朋友
ArrayList<String> commonFriends = new ArrayList<>();
//获取 value 值
String value = entry2.getValue();
//将value分割成数组
String[] firends = value.split(",");
//6 循环判断这些 “朋友” 在第一层value中是否存在
for (String friend : firends) {
if (entry1.getValue().contains(friend)) {
//7 如果存在,将该 “朋友” 存入集合
commonFriends.add(friend);
}
}
//8 过滤两个博主之间没有共同好友的情况
if (!commonFriends.isEmpty()) {
System.out.println(entry1.getKey() + "->" + entry2.getKey() + "\t" + commonFriends);
context.write(new Text(entry1.getKey() + "->" + entry2.getKey()), new Text(commonFriends.toString()));
}
}
flags.add(entry1.getKey());
}
}
}
四.运行结果
结果文件一共74行,有17行没有共同好友的记录被过滤,共计91行
A->B [C, E]
A->C [F, D]
A->D [E, F]
A->E [B, C, D]
A->F [B, C, D, E, O]
A->G [C, D, E, F]
A->H [C, D, E, O]
A->I [O]
A->J [B, O]
A->K [C, D]
A->L [D, E, F]
A->M [E, F]
B->C [A]
B->D [A, E]
B->E [C]
B->F [A, C, E]
B->G [A, C, E]
B->H [A, C, E]
B->I [A]
B->K [A, C]
B->L [E]
B->M [E]
B->O [A]
C->D [A, F]
C->E [D]
C->F [A, D]
C->G [A, D, F]
C->H [A, D]
C->I [A]
C->K [A, D]
C->L [D, F]
C->M [F]
C->O [A, I]
D->E [L]
D->F [A, E]
D->G [A, E, F]
D->H [A, E]
D->I [A]
D->K [A]
D->L [E, F]
D->M [E, F]
D->O [A]
E->F [B, C, D, M]
E->G [C, D]
E->H [C, D]
E->J [B]
E->K [C, D]
E->L [D]
F->G [A, C, D, E]
F->H [A, C, D, E, O]
F->I [A, O]
F->J [B, O]
F->K [A, C, D]
F->L [D, E]
F->M [E]
F->O [A]
G->H [A, C, D, E]
G->I [A]
G->K [A, C, D]
G->L [D, E, F]
G->M [E, F]
G->O [A]
H->I [A, O]
H->J [O]
H->K [A, C, D]
H->L [D, E]
H->M [E]
H->O [A]
I->J [O]
I->K [A]
I->O [A]
K->L [D]
K->O [A]
L->M [E, F]