zoukankan      html  css  js  c++  java
  • MapReduce实战:查找相同字母组成的单词

    1、项目需求

        一本英文书籍包含成千上万个单词,现在我们需要在大量的单词中,找出相同字母组成的所有单词

    2、数据集

    下面是一本英文书籍截取的一部分单词内容(书籍内容是随意写的,主要目的是实现这种需求

    3、分析

        1、先看如下图

        在上图中,cat、act是相同字母组成的单词,tar、rat也是相同字母组成的单词,bar只有一个,它不显示,因为没有和它是相同字母组成单词

        2、基于以上分析,我们通过以下几步完成

            1、在Map阶段,对每个word(单词)按字母进行排序生成sortedWord,然后输出key/value键值对(sortedWord,word)。

            2、在Reduce阶段,统计出每组相同字母组成的所有单词

        3、示意流程

    1、以下面几个单词为例,找出相同字母组成的单词。

                cat

                tar

                bar

                act

                rat

            2、经过map阶段处理后,如下

                key        value

                act        cat

                art        tar

                abr        bar

                act        act

                art        rat

                在这一阶段中,对每个单词按字母进行排序生成sortedWord,并将其作为key,然后输出key/value键值对(sortedWord,word):

            3、经过框架处理后,如下(这一步框架自动帮我们实现)

                act,[act, cat]

                art,[cat, rat]

                abr,[bar]

        在一阶段,根据key(sortedWord)进行分组

            4、经过reduce阶段处理,如下

                key        value

                act        act,cat

                art        rat,tar

                在一阶段,拼接一组中的所有单词,然后输出;需要说明一点,(abr,[bar])这个被过滤掉了,因为这一组中就它一个

    4、实现

    上面已经分析完毕,下面我们就着手实现它。这里需要编写三块代码内容:

        1、map 函数、

         2、reduce函数

         3、一些用来运行作业的代码。

      1、map 函数

    下面我们来编写 Mapper 类,实现 map() 函数

     1 /*
     2 
     3 * 排序、分组
     4 
     5 */
     6 public static class AnagramMapper extends Mapper<LongWritable, Text, Text, Text>{
     7 
     8     public void map(LongWritable key, Text value,Context context) throws IOException , InterruptedException {
     9 
    10         // 将Text转换成String
    11 
    12         String text = value.toString();
    13 
    14         
    15 
    16         // 将String转换成字符数组,为排序作准备
    17 
    18         char[] textCharArr = text.toCharArray();
    19 
    20         
    21 
    22         // 使用 Arrays对数组进行排序
    23 
    24         Arrays.sort(textCharArr);
    25 
    26         
    27 
    28         // 排序后的字符串
    29 
    30         String sortedText = new String(textCharArr);
    31 
    32         
    33 
    34         context.write(new Text(sortedText), value);
    35 
    36     }
    37 }

    这个 Mapper 类是一个泛型类型,它有四个形参类型,分别指定 map 函数的输入键、输入值、输出键和输出值的类型。 就本示例来说,输入键是一个长整数偏移量(LongWritable),输入值是一行文本(Text), 其次输出键、输出值也是文本类型(Text)的。

    map() 方法的输入是一个键(key)和一个值(value),我们首先将 Text 类型的 value 转换成 Java 的 String 类型, 之后将其转换成字符数组,通过Arrays.sort方法对该字符数组进行排序,然后将排序后的单词作为key,源单词作为value输出。map() 方法还提供了 Context 实例用于将输出内容进行写入。 在这种情况下,我们将key、value封装成Text对象,并将它们进行写入。

      2、reduce函数

    下面我们来编写 Reducer类,实现reduce函数

    /*
    
    * 统计相同字母组成的单词
    
    */
    
    public static class AnagramReduce extends Reducer<Text, Text, Text, Text>{
    
        public void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
    
            //
    
            StringBuilder value = new StringBuilder();
    
            // 计数
    
            int count = 0;
    
            // 拼接单词
    
            for(Text text : values){
    
                // 分割符,
    
                if(value.length() > 0){
    
                    value.append(",");
    
                }
    
                value.append(text);
    
                
    
                // 计数
    
                count++;
    
            }
    
            
    
            // 因为我们要统计相同字母组成的单词,所以相同字母组成的单词个数大于等于2才会输出
    
            if(count > 1){
    
                context.write(key, new Text(value.toString()));
    
            }
    
        }
    
    }

    同样,reduce 函数也有四个形式参数类型用于指定输入和输出类型。reduce 函数的输入类型必须匹配 map 函数的输出类型:即 Text 类型和Text类型。 在这种情况下,reduce 函数的输出类型也是 Text 和 Text 类型。在 map 的输出结果中,所有相同key的键值对被分配到同一个reduce执行,通过迭代来拼接所有单词,并将一组中只有一个单词的过滤掉

        3、一些用来运行作业的代码

     1 public int run(String[] arg0) throws Exception {
     2 
     3     // 加载配置
     4 
     5     Configuration conf = new Configuration();
     6 
     7     
     8 
     9     // 输出目录,如果存在就删除
    10 
    11     Path path = new Path(arg0[1]);
    12 
    13     FileSystem fileSystem = path.getFileSystem(conf);
    14 
    15     if(fileSystem.isDirectory(path)){
    16 
    17         fileSystem.delete(path, true);
    18 
    19     }
    20 
    21       
    22 
    23     // 创建job对象
    24 
    25     Job job = new Job(conf,"anagram");
    26 
    27     job.setJarByClass(Anagram.class);
    28 
    29     
    30 
    31     // 指定输入、输出目录
    32 
    33     FileInputFormat.addInputPath(job, new Path(arg0[0]));
    34 
    35     FileOutputFormat.setOutputPath(job, new Path(arg0[1]));
    36 
    37     
    38 
    39     // 指定mapper、reduce
    40 
    41     job.setMapperClass(AnagramMapper.class);
    42 
    43     job.setReducerClass(AnagramReduce.class);
    44 
    45     
    46 
    47     // 指定mapper、reduce的输出类型
    48 
    49     job.setOutputKeyClass(Text.class);
    50 
    51     job.setOutputValueClass(Text.class);    
    52 
    53     
    54 
    55     // 提交作业
    56 
    57     return job.waitForCompletion(true) ? 0: 1;
    58 
    59 }
     1 public static void main(String[] args) throws Exception {
     2 
     3         String[] arg0 = {
     4 
     5         "hdfs://ljc:9000/buaa/anagram",
     6 
     7         "hdfs://ljc:9000/buaa/anagram/out"
     8 
     9     };
    10 
    11     
    12 
    13     // 执行mapperreduce
    14 
    15     int status = ToolRunner.run(new Configuration(), new Anagram(), arg0);
    16 
    17     
    18 
    19     System.exit(status);
    20 }

    Configuration 类读取 Hadoop 的配置文件,如 site-core.xml、mapred-site.xml、hdfs-site.xml 等。

    Job 对象指定作业执行规范,我们可以用它来控制整个作业的运行。我们在 Hadoop 集群上运行这个作业时,要把代码打包成一个JAR文件(Hadoop在集群上发布这个文件)。 不必明确指定 JAR 文件的名称,在 Job 对象的 setJarByClass 方法中传递一个类即可,Hadoop 利用这个类来查找包含它的 JAR 文件,进而找到相关的 JAR 文件;构造 Job 对象之后,需要指定输入和输出数据的路径

    以下特别说明:

        1、调用 FileInputFormat 类的静态方法 addInputPath() 来定义输入数据的路径,这个路径可以是单个的文件、一个目录(此时,将目录下所有文件当作输入)或符合特定文件模式的一系列文件。由函数名可知,可以多次调用 addInputPath() 来实现多路径的输入。

        2、调用 FileOutputFormat 类中的静态方法 setOutputPath() 来指定输出路径(只能有一个输出路径)。这个方法指定的是 reduce 函数输出文件的写入目录。 在运行作业前该目录是不应该存在的,否则 Hadoop 会报错并拒绝运行作业。这种预防措施的目的是防止数据丢失(长时间运行的作业如果结果被意外覆盖,肯定是件可怕的事情)。

        3、通过 setMapperClass() 和 setReducerClass() 指定 map 类型和reduce 类型。

        4、通过setOutputKeyClass() 和 setOutputValueClass() 控制 map 和 reduce 函数的输出类型,正如本例所示,这两个输出类型一般都是相同的。如果不同,则通过 setMapOutputKeyClass()和setMapOutputValueClass()来设置 map 函数的输出类型。

        5、输入的类型通过 InputFormat 类来控制,我们的例子中没有设置,因为使用的是默认的 TextInputFormat(文本输入格式)。

        6、Job 中的 waitForCompletion() 方法提交作业并等待执行完成。该方法中的布尔参数是个详细标识,所以作业会把进度写到控制台。 waitForCompletion() 方法返回一个布尔值,表示执行的成(true)败(false),这个布尔值被转换成程序的退出代码 0 或者 1。

    5、结果

    6、其他问题

    1、hadoop环境配置

    2、基于Eclipse的hadoop开发环境配置

    3、代码及数据

    如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
    如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
    如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【刘超★ljc】。

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    最近在搞微信支付,说说流程,免得遗忘
    好记性不如烂笔头-Mysql查找如何判断字段是否包含某个字符串
    用jquery操作字体颜色覆盖当前页面的css设置
    页面白屏并且报错PHP Parse error: syntax error, unexpected end of file in 试了很久总算解决了
    bootstrap的datetimepicker.js的结束时间大于开始时间,当前日期之前的代码
    使用ClassLoader类装载器获取系统资源
    关于App class loader的总结
    ClassLoader 详解及用途(写的不错)
    webRequest
    Tomcat学习之ClassLoader
  • 原文地址:https://www.cnblogs.com/codeOfLife/p/5372731.html
Copyright © 2011-2022 走看看