内容简介
一、需求分析
- 社交软件的盛行让我们的生活越来越便捷,我们经常使用社交软件添加好友,即每个人会有自己的好友列表。如果用字母表示人,字母与字母之间连线表示这两个人互为好友关系,那么部分社交关系图可能是这样的:
然而现实中的社交关系远比这个复杂得多,有时候,我们需要知道人与人之间的共同好友,这个是一个非常有价值的信息,因为可以根据此信息为用户推荐好友。所谓共同好友,就是指每两个人之间的共同好友。例如在图中,A与B他们都认识E,所以A与B存在一个共同好友E,现在要求出所有人,两两之间的所有共同好友。 - 现在对问题进行抽象处理,使用如下格式表示关系,如就图中而言,A->C,E,K代表了A与C,E,K互为好友关系,现在给定一批这样的数据,然后编写MapReduce代码寻找两两之间的共同好友,就图中A和B两个人而言,输出结果是这样的:A-B C,E,K表示A和B两个有共同好友 C,E,K。
二、算法分析
- 第一阶段的MapReduce作业,原数据如下:
给定类似于这种数据,求解共同好友,由图中我们发现,两个人即使互不认识,他们也可能有共同好友,所以分析问题的时候不能顺着去思考,可以这样思考,如果存在一个人E是两个人A和B的共同好友,那么反过来,对于E而言,他肯定存在两个好友A和B,这是确定的,所以我们第一步先将 A->Q,I,Y,U,P映射为(Q,A),(I,A),(Y,A),(U,A),(P,A),把每一行都进行这样映射之后,对Key聚合,Value用逗号进行隔开,得到这样的结果:(I,“A,B,C”),可以知道,A,B,C三个人都存在共同好友I。作业的第一阶段完成。A->Q,I,Y,U,P B->Y,I,E,X,H,T C->I,W,O,U,Y,H,G
- 第二阶段的MapReduce作业:
第一阶段的MapReduce作业得到类似于(I,“A,B,C”)这样的元组之后,将其作为数据源输入。首先将Value拆分,然后通过组合,输出(A-B,I),(A-C,I),(B-C,I),意思是A和B存在一个共同好友I,以此为Key,进行聚合,Value用逗号隔开,就得到了我们想要的结果。值得注意的是:对于第一阶段聚合的元组,我们对其Value进行拆分重组是按照顺从第一个字母与第二个字母组合,然后第一个字母与第三个字母组合…,这样可能存在这样的Key:A-B,B-A,这两者显示是代表同一组关系,但是可能因为Value的里的顺序不一致导致重组不一样,所以,需要对拆分后的Value排序后再重组。
三、编写代码
1.构建Java工程,引入Maven支持
- 使用的Hadoop版本是 2.6.0-cdh5.7.0,开发工具是IEDA2018
- 完整的Maven依赖如下
<properties> <hadoop.version>2.6.0-cdh5.7.0</hadoop.version> </properties> <repositories> <repository> <id>cloudera</id> <url>https://repository.cloudera.com/artifactory/cloudera-repos</url> </repository> </repositories> <dependencies> <dependency> <groupId>org.apache.hadoop</groupId> <artifactId>hadoop-client</artifactId> <version>${ hadoop.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> </dependency> </dependencies>
2.编写第一阶段MapReduce作业代码
- 创建并编写MapStepOne类,代码如下:
/** * 求解社交共同好友第一阶段map */ public class MapStepOne extends Mapper<LongWritable, Text,Text,Text> { @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { //获得文本 String line = value.toString(); //获得本人 String person = line.split("->")[0]; //获得朋友 String[] friends = line.split("->")[1].split(","); //将朋友作为Key,本人作为Value for(String friend : friends){ context.write(new Text(friend),new Text(person)); } } }
- 创建并编写ReduceStepOne类,代码如下:
/** * 求解社交共同好友第一阶段reduce */ public class ReduceStepOne extends Reducer<Text,Text,Text,Text> { @Override protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException { //聚合friend String persons = ""; for (Text friend : values){ persons += friend.toString() + ","; } //将最后的逗号去除 persons = persons.