简单实现单词统计,通俗注解(快速学习)——MapReduce学习1

版权声明:君若不学,则何以成?本文为博主原创文章,欢迎转载(记得注明出处)。 https://blog.csdn.net/hu_belif/article/details/82595361

今天自己写了一下简单的单词统计的MapReduce算法程序,在这里分享一下,为还在为此迷茫的伙伴指引方向同时也希望路过的大佬帮我指点一下不足之处,感谢。

论hdfs单词统计学习的重要性:

在Hadoop学习过程中,单词统计作为一个最基本的案例,非常简单实用,是每一个入门菜鸟必须要掌握的一个例子,可以通过这个简单的小案例了解Hadoop的基本运行原理和MapReduce程序的开发流程。

下面开搞,先看一下map的写法:

package com.bw.map;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;
import java.util.StringTokenizer;

/**
 * word count map
 *
 * @Author H.rosy
 * @Create 2018-09-09  19:31
 */
public class countMap extends Mapper<LongWritable,Text,Text,IntWritable>{
    /**
     * 首先要继承mapper这个类,然后定义好泛型
     * Mapper需要四个泛型参数:
     * KEYIN:输入参数:默认是要处理的文本中的某一行的偏移量
     * VALUEIN:输入参数:要处理的某一行文本内容
     * VALUEOUT:输出给Reduce的数据类型
     * KEYOUT:输出给Reduce的偏移量
     * 第一个是LongWritable,固定就是这个类型,表示每一行单词的起始位置(单位是字节)
     * 第二个是Text,表示每一行的文本内容.
     * 第三个是Text,表示单词
     * 第四个是LongWritable,表示单词的出现次数
     *
     * 由于需要网络传输,故参数需要序列化
     * 但是Java自带的序列化会携带一些冗余信息,不利于大量的网络传输
     * 所以Hadoop对Long,String进行了封装,变为LongWritable,Text
     */
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {

        //将获取到的文本类型转换为String类型
        String values = value.toString();
        //通过StringTokenizer进行字符串拆分,默认就是空格
        StringTokenizer sk = new StringTokenizer(values);
        //循环获取拆分后的值
        while(sk.hasMoreElements()){
            //转交给reduce进行分组计算
            //write(这个要与上面定义的泛型类型一致,第二个类型默认为一个)
            context.write(new Text(sk.nextToken()),new IntWritable(1));
        }
    }
}

下面是reduce的写法:

package com.bw.reduce;

import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

/**
 * word count reduce
 *
 * @Author H.rosy
 * @Create 2018-09-09  19:40
 */
public class countReduce extends Reducer<Text,IntWritable,Text,IntWritable> {
    /**
     * 继承reducer类
     *
     *
     */
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
        //首先定义一个计数器
        int count  = 0;
        for (IntWritable value : values) {
            //用累加器记录相同单词的个数
            count += value.get();
        }
        //输出最终结果
        context.write(new Text(key),new IntWritable(count));
    }
}

最后是main函数:

package com.bw.demo;


import com.bw.map.countMap;
import com.bw.map.sortMap;
import com.bw.reduce.countReduce;
import com.bw.reduce.sortReduce;
import com.bw.sort.MySort;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;

/**
 * This is my test 
 *
 * @Author 
 * @Create 2018-09-09  19:43
 */
public class MyDemo {
    /**
     * 首先创建一个静态变量区
     *
     * @param args
     */
    static Configuration conf = new Configuration();
    static Job job = null;
    static FileSystem fs = null;
    static String uri = "hdfs://192.168.132.130:9000";

    static {//静态代码块
        try {
            conf.setBoolean("dfs.support.append", true);
            fs = FileSystem.get(URI.create(uri), conf);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        //公共变量区
        String input = uri.concat("/user/a.txt");//本人需要做统计的文本放在hdfs的这个目录下
        String output = uri.concat("/output");//统计好结果后的文件存放目录
       

        {//初始化job任务区
            job = Job.getInstance(conf);//定义一个job任务
            job.setJobName("wordCount");//添加工作名字

            job.setMapperClass(countMap.class);//添加map类映射
            job.setReducerClass(countReduce.class);//添加reduce映射

            job.setMapOutputKeyClass(Text.class);//设置map阶段的输出key类型
            job.setMapOutputValueClass(IntWritable.class);//设置map阶段的输出value类型

            job.setOutputKeyClass(Text.class);//设置最终阶段的输出key类型
            job.setOutputValueClass(IntWritable.class);//设置最终阶段的输出value类型

            checkFileExists(new Path[]{new Path(output)});//检测文件是否存在
            FileInputFormat.setInputPaths(job, new Path(input));//指定操作路径
            FileOutputFormat.setOutputPath(job, new Path(output));//指定操作路径
            job.waitForCompletion(true);//提交任务
        }


        {//结果展示模块
           
            {//统计结果展示块
                String alert = "-------------------------------下面是统计结果--------------------------";
                getResult(new Path(sortInput), alert);
            }
            
        }
        {
            closeResurce();//关闭资源
        }
    }


    synchronized static boolean getResult(Path path, String alert) throws IOException {
        FSDataInputStream open = fs.open(path);//打开目标路径的文件
        BufferedReader reader = new BufferedReader(new InputStreamReader(open, "utf-8"));//设置缓冲区
        String res = "";
        System.err.println(alert);//输出提示信息
        while ((res = reader.readLine()) != null) {//循环按行读取文本内容并赋值给res
            System.out.println(res);//输出统计后的结果
        }
        reader.close();//关闭资源
        return true;
    }

    static void checkFileExists(Path... paths) throws IOException {//查看文件是否存在,避免文件重复存在的错误
        for (Path path : paths) {
            boolean exists = fs.exists(path);
            if (exists) {
                fs.delete(path, true);
            }
        }
    }


    static void closeResurce() throws IOException {//关闭fs资源
        fs.close();
    }

好了,这样就可以直接跑了,测试了一下,完美运行。

猜你喜欢

转载自blog.csdn.net/hu_belif/article/details/82595361
今日推荐