题目:以下是qq的好友列表数据,冒号前是一个用户,冒号后是该用户的所有好友(数据中的好友关系是单向的)
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
H:E,F,G
O:A,H,I,J
P:H,L,S,F,E,A,C,D
求出哪些人两两之间有共同好友,及他俩的共同好友都有谁?
解题思路:
第一步:先找出一个用户是哪些用户的共同好友(比如C是哪些用户的共同好友,以上题目中的C是用户A,B,E,F,G,H,K的共同好友,所以AB的共同好友为C,AE的共同好友为C,以此类推。。。)
第二步:经过第一步推算,得到AE的 共同好友还有D,最后将AE的共同好友合并得到C,D,这只是举个例子,他们的共同好友还有很多,即将两两用户作为key,好友作为value,以此类推,因此需要写两个mapreduce。
代码实现
第一个mapreduce
public class IdenticalFriendsStepOne {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
Configuration configuration = new Configuration();
Job job = Job.getInstance(configuration);
//指定本程序的jar包所在的本地路径
job.setJarByClass(IdenticalFriendsStepOne.class);
job.setMapperClass(IdenticalFriendsStepOneMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
FileInputFormat.setInputPaths(job, new Path(args[0]));
job.setReducerClass(IdenticalFriendsStepOneReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.waitForCompletion(true);
}
public static class IdenticalFriendsStepOneMapper extends Mapper<LongWritable, Text, Text, Text>{
@Override
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
//A:B,C,D,F,E,O
//获取文本一行数据,转化为字符串
String line = value.toString();
//将字符串以 :号分割,转换成数组
String[] person_friends = line.split(":");
//数组的第一个值为用户
String person = person_friends[0];
//数组的第二个值为用户的好友,即B,C,D,F,E,O
String friends = person_friends[1];
//将用户的好友遍历出来
for(String friend : friends.split(",")) {
//输出<好友,用户>
context.write(new Text(friend), new Text(person));
}
}
}
public static class IdenticalFriendsStepOneReducer extends Reducer<Text, Text, Text, Text>{
@Override
protected void reduce(Text friend, Iterable<Text> persons, Context context)
throws IOException, InterruptedException {
StringBuffer sb = new StringBuffer();
for(Text person : persons) {
sb.append(person).append(",");
}
context.write(friend, new Text(sb.toString()));
}
}
}
结果输出
A H,I,B,C,D,F,O,G,P,K,
B J,F,A,E,
C P,H,F,A,K,B,G,E,
D P,L,K,H,F,E,C,A,G,
E P,D,G,H,H,B,F,A,L,
F D,L,H,A,C,P,G,
G H,
H P,O,
I O,C,
J O,
K B,
L D,E,P,
M F,E,
O J,H,A,I,F,
S P,
为了防止b–>c和c–>b这样同一对朋友的重复,所以,下面基于这个结果处理的时候,需要进行排序,这样就能达到没有重复朋友对的出现。
第二个mapreduce
public class IdenticalFriendsStepTwo {
public static class IdenticalFriendsStepTwoMapper extends Mapper<LongWritable, Text, Text, Text> {
//拿到的数据是上一个步骤的输出结果
//A H,I,B,C,D,F,O,G,P,K,(即A是H,I,B,C,D,F,O,G,P,K这些用户的好友)
//友 人,人,人
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
String[] friend_persons = line.split("\t");
String friend = friend_persons[0];
String[] persons = friend_persons[1].split(",");
Arrays.sort(persons);
for (int i = 0; i < persons.length - 1; i++) {
for (int j = i + 1; j < persons.length; j++) {
// 发出 <人-人,好友> ,这样,相同的“人-人”对的所有好友就会到同1个reduce中去
context.write(new Text(persons[i] + "-" + persons[j]), new Text(friend));
}
}
}
}
public static class IdenticalFriendsStepTwoReducer extends Reducer<Text, Text, Text, Text> {
@Override
protected void reduce(Text person_person, Iterable<Text> friends, Context context) throws IOException, InterruptedException {
StringBuffer sb = new StringBuffer();
for (Text friend : friends) {
sb.append(friend).append(" ");
}
context.write(person_person, new Text(sb.toString()));
}
}
public static void main(String[] args) throws Exception {
Configuration configuration = new Configuration();
Job job = Job.getInstance(configuration);
job.setJarByClass(IdenticalFriendsStepTwo.class);
job.setMapperClass(IdenticalFriendsStepTwoMapper.class);
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(Text.class);
FileInputFormat.setInputPaths(job, new Path(args[0]));
//job.setReducerClass(IdenticalFriendsStepTwoReducer.class);
job.setReducerClass(IdenticalFriendsStepTwoReducer.class);
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
FileOutputFormat.setOutputPath(job, new Path(args[1]));
job.waitForCompletion(true);
}
}
输出结果
A-B E C
A-C D F
A-D E F
A-E B D C
A-F E O B C D
A-G E C D F
A-H D E O F E C
A-I O
A-J B O
A-K D C
A-L D F E
A-P F D E C
B-C A
B-D E A
B-E C
B-F A C E
B-G C E A
B-H E A C E
B-I A
B-K C A
B-L E
B-O A
B-P A E C
C-D A F
C-E D
C-F D A
C-G F D A
C-H F A D
C-I A
C-K D A
C-L F D
C-O A I
C-P D F A
D-E L
D-F E A
D-G A F E
D-H F E E A
D-I A
D-K A
D-L E F
D-O A
D-P A L F E
E-F M D C B
E-G C D
E-H C D
E-J B
E-K C D
E-L D
E-P D L C
F-G A D C E
F-H E A C E O D
F-I A O
F-J O B
F-K A D C
F-L E D
F-O A
F-P A E C D
G-H D E E C A F
G-I A
G-K A C D
G-L E D F
G-O A
G-P F E C A D
H-H E
H-I A O
H-J O
H-K D C A
H-L F E D E
H-O A
H-P F A E C D E
I-J O
I-K A
I-O A
I-P A
K-L D
K-O A
K-P C D A
L-P E F D
O-P A H