大数据之Hadoop_MapReduce自定义输出和输入类型

自定义输出和输入类型介绍

我们在使用MapReduce处理需要两次聚合的数据时,我们会进行两次输出,第二次输出的结果是读取第一次输出的结果进程聚合处理的,但我们只需要看到第二次的聚合的结果就可以了,第一次聚合的结果我们是否能看懂都无所谓,
此时我们在进行第一次输出时,我们可以将输出类型由原来(当我们未自定义时默认的输入和输出类型都是TextInputFormat和TextOutputFormat)改为SequenceFileOutputFormat,Sequence相对于Text类型处理速度较快,且更节约内存,然后第二次读取的时候,输入类型也设定为SequenceFileInputFormat,但注意第二次聚合时map端的输入范型应该为和第一次聚合后输出的类型一致,即第一次输出的什么key和value,第二次接收的就是什么key和value

代码实现

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
import org.apache.hadoop.mapreduce.lib.input.SequenceFileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.SequenceFileOutputFormat;
import java.io.IOException;
import java.util.*;

public class Word {
    /**
     * Mapper类,读取文件,每次读一行
     * 最后以文件名和单词进行拼接作为key,数量为value,输出
     */
    static class WordCountMapper extends Mapper<LongWritable, Text,Text, IntWritable> {
        String fileName ;
        Text k = new Text();
        IntWritable v = new IntWritable(1);
        @Override//setup方法,该方法会在map方法循环执行前,被执行一次
        protected void setup(Context context) throws IOException, InterruptedException {
            //获取读取的文件对象
            FileSplit file = (FileSplit)context.getInputSplit();
            //获取到文件的路径和文件名
            fileName = file.getPath().getName();
        }

        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            try {//将切割流程try起来,避免脏数据影响程序运行
                String s = value.toString();
                //以空格切割,将每行的单词分开
                String[] split = s.split("\\s+");
                //数组内每个元素都是一个单词
                for (String s1 : split) {
                    //将每个单词,和他的文件名进行拼接成K,输出到缓存区
                    String fileWord = s1+"-"+fileName;
                    k.set(fileWord);
                    context.write(k,v);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * Reduce类,将分区排序后的数据进行聚合后输出
     */
    static class WordCountReduce extends Reducer<Text, IntWritable,Text, IntWritable> {

        IntWritable v = new IntWritable();
        @Override
        protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
                int count = 0;
            for (IntWritable value : values) {
                //将迭代器中的1进行叠加聚合
                count++;
            }
            v.set(count);
            context.write(key,v);
        }
    }

    /**
     * 将第一次处理后的文件再读一次,进行二次聚合
     * 第一次聚合后输出的文件类型为sequence类型的,所以接收时,要与上次输出的类型保持一致
     */
    static class WordCountMapper2 extends Mapper<Text,IntWritable,Text,Text>{
        Text k = new Text();
        Text v = new Text();
        @Override
        protected void map(Text key, IntWritable value, Context context) throws IOException, InterruptedException {
            try {

                String s = key.toString()+" "+value.toString();
                //用"-"将单词和文件名切开
                String[] split = s.split("-");
                //以单词为key,文件名和数量为value输出到缓存区
                k.set(split[0]);
                v.set(split[1]);
                context.write(k,v);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 将缓存区内分区排序后的数据进行聚合运算
     */
    static class WordCountReduce2 extends Reducer<Text,Text,Text,Text>{

        Text v = new Text();
        @Override
        protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
            Map<String, Integer> map = new HashMap<>();
            //将按单词,分组后的文件名加数量的迭代器遍历放入map集合中
            for (Text value : values) {
                String s = value.toString();
                String[] split = s.split("\\s");
                map.put(split[0],Integer.parseInt(split[1]));
            }
            System.out.println(map.size());
            //将map集合转换成set单链集合
            Set<Map.Entry<String, Integer>> entries = map.entrySet();
            //将set集合转换为list集合
            ArrayList<Map.Entry<String, Integer>> list = new ArrayList<>(entries);
            //对map集合的value逆序排序
            list.sort(new Comparator<Map.Entry<String, Integer>>() {
                @Override
                public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                    return o2.getValue()-o1.getValue();
                }
            });
            StringBuilder sb = new StringBuilder();
            //遍历排序后的list集合,放入key和value中输出
            for (Map.Entry<String, Integer> l : list) {

                sb.append(l.getKey()+"-"+l.getValue()+" ");
                //去除sb的最后一个空格
                String vs = sb.toString().trim();
                String vv = sb.toString();
                v.set(vv);
            }
            context.write(key,v);
        }
    }

    /**
     * 启动类
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        Job job = Job.getInstance(conf, "m");
        //第一次聚合的map端和reduce端
        //job.setMapperClass(WordCountMapper.class);
        //job.setReducerClass(WordCountReduce.class);

        //第二次聚合的map端和reduce端
        job.setMapperClass(WordCountMapper2.class);
        job.setReducerClass(WordCountReduce2.class);

        //当map和reduce类输出的类型相同时,可以省略以下两句
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(Text.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);

        //设置第一次输出的文件类型为sequence类型的
        //job.setOutputFormatClass(SequenceFileOutputFormat.class);
        //第一次聚合的输入和输出路径
        //FileInputFormat.setInputPaths(job,new Path("D:\\txt\\mrdata\\index\\input"));
        //FileOutputFormat.setOutputPath(job,new Path("D:\\txt\\mrdata\\index\\output20"));

        //设置第二次输入的文件类型为sequence类型的
        job.setInputFormatClass(SequenceFileInputFormat.class);
        //第二次聚合的输入和输出路径
        FileInputFormat.setInputPaths(job,new Path("D:\\txt\\mrdata\\index\\output26"));
        FileOutputFormat.setOutputPath(job,new Path("D:\\txt\\mrdata\\index\\output27"));

        job.waitForCompletion(true);
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值