Hadoop编程——第五章:(6)MapReduce自定义排序案例

本文详细展示了如何使用Hadoop MapReduce框架,通过自定义CovidCountBean2对象和重写Comparator,对美国各州的COVID-19确诊病例数进行倒序排序。通过CovidSortSumMapper和Reducer实现数据处理,最后输出按确诊数递减的州列表。

美国新冠疫情COVID-19

案例2:各州累计病例数呈倒排统计

将美国每个州state的确诊案例数进行倒序排序。

一、需求分析

如果你的需求中需要根据某个属性进行排序,不妨把这个属性作为key。因为MapReduce中key有默认排序行为的。可使用案例1的输出结果表作为本案例的输入表。

1、如果你的需求是正序,并且数据类型是Hadoop封装好的类型。这种情况下不需要任何修改,直接使用lHadoop类型作为kcy即可。因为Hadoop封装好的类型已经实现了排序规则。

2、如果你的需求是倒序,或者数据类型是自定义对象。需要重写排序规则。对象实现Comparable接口重写CompareTo方法。

把封装数据作为key,系统默认排序后,再把州和封装数据倒过来输出
在这里插入图片描述

二、代码实现

(一)CovidSortSumMapper

package mapreduce.CovidSortSum;

import beans.CovidCountBean2;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

/**
 * Alabama	252900	3638
 */
public class CovidSortSumMapper extends Mapper<LongWritable, Text, CovidCountBean2, Text> {

    /**
     * 3、创建输出对象
     */
    CovidCountBean2 outKey = new CovidCountBean2();
    Text outValue = new Text();


    /**
     * 1、重写map父类方法:map回车
     */

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {

        /**
         * 2、读取一行数据进行切割
         */
        String[] fields = value.toString().split("\t");

        /**
         * 4、提取数据 州、确诊数、死亡数
         */
        outKey.set(Long.parseLong(fields[1]),Long.parseLong(fields[2]));
        outValue.set(fields[0]);

        /**
         * 5、输出结果
         */
        context.write(outKey,outValue);
    }
}

注意LongWritable, Text, CovidCountBean2, Text,第二个是一条数据

(二)CovidSortSumReducer

package mapreduce.CovidSortSum;

import beans.CovidCountBean2;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

public class CovidSortSumReducer extends Reducer<CovidCountBean2, Text,Text,CovidCountBean2> {


    @Override
    protected void reduce(CovidCountBean2 key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
        /**
         * 排序好之后 reduce会进行分组操作,分组规则是判断key是否相等
         * 本业务中,使用自定义对象作为key,并且没有重写分组规则,默认就会比较对象的地址,导致每个kv队就是一组
         * <CovidCountBean2,加州> --------------><CovidCountBean2,Iterable[加州]>
         * <CovidCountBean2,德州> --------------><CovidCountBean2,Iterable[德州]>
         */
        Text outValues = values.iterator().next();
        context.write(outValues,key);

    }
}

(三)CovidSortSumDriver

package mapreduce.CovidSortSum;

import beans.CovidCountBean2;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
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;

/**
 * 该类就是MapReduce程序客户端驱动类
 *     主要是构造Job对象实例
 *     指定各种组件属性:mapper、reducer类,输入输出的数据类型,输入输出的数据路径,提交job作业(job.submit())
 */
public class CovidSortSumDriver {
    public static void main(String[] args) throws Exception {

        //创建驱动类
        Configuration conf = new Configuration();

        //本地模式运行可以直接指定路径,或者配置Configurations
//        args = new String[] {"F:/MyBigData/Hadoop/data/mr_wordcount/input ","F:/MyBigData/Hadoop/data/mr_wordcount/output"};

        //设置mapreduce程序的运行模式
//        conf.set("mapreduce.framework.name","local");

        //构造job作业的实例,参数(配置对象,job名字)
        Job job = Job.getInstance(conf, CovidSortSumDriver.class.getSimpleName());

        //设置mr程序运行的主类
        job.setJarByClass(CovidSortSumDriver.class);

        //设置本次mr程序的mapper类型、reducer类型
        job.setMapperClass(CovidSortSumMapper.class);
        job.setReducerClass(CovidSortSumReducer.class);

        //指定mapper阶段输出的key value数据类型
        job.setMapOutputKeyClass(CovidCountBean2.class);
        job.setMapOutputValueClass(Text.class);

        //指定reducer阶段输出的key value数据类型,也是mr程序最终的输出数据类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(CovidCountBean2.class);

        //配置本次作业的输入数据路径和输出数据路径
        Path inputPath = new Path(args[0]);
        Path outputPath = new Path(args[1]);

        //todo 默认组件 TextInputFormat TextOutputFormat
        FileInputFormat.setInputPaths(job, inputPath);
        FileOutputFormat.setOutputPath(job,outputPath);

        //todo 判断输出路径是否已经存在,如果已经存在,先删除
        FileSystem fs = FileSystem.get(conf);
        if(fs.exists(outputPath)){
            fs.delete(outputPath,true); //递归删除
        }

        //最终提交本次job作业
        //job.submit();
        //采用waitForCompletion提交job,参数表示是否开启实时监视追踪作业的执行情况
        boolean resultFlag = job.waitForCompletion(true);
        //退出程序 和job结果进行绑定, 0是正常退出,1是异常退出
        System.exit(resultFlag ? 0: 1);

    }
}

(四)CovidCountBean2

package beans;

import org.apache.hadoop.io.WritableComparable;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

/**
 * 自定义对象作为数据类型在MR中传递
 * 一定要实现Hadoop的序列化机制:接口Writable, ctrl+i 实现两个方法
 */


public class CovidCountBean2 implements WritableComparable<CovidCountBean2> {

    //1、封装私有的属性
    private long cases; //确诊病例数
    private long deaths;  //死亡病例数

    //2、有参无参构造
    public CovidCountBean2() {
    }

    public CovidCountBean2(long cases, long deaths) {
        this.cases = cases;
        this.deaths = deaths;
    }

    /**
     *对有参构造进行修改,提供一个set方法
     * 自己封装对象的set方法,用于对象属性赋值
     */
    public void set(long cases, long deaths) {
        this.cases = cases;
        this.deaths = deaths;
    }

    //3、set和get方法

    public long getCases() {
        return cases;
    }

    public void setCases(long cases) {
        this.cases = cases;
    }

    public long getDeaths() {
        return deaths;
    }

    public void setDeaths(long deaths) {
        this.deaths = deaths;
    }

    //4、实现对象的方法

//    @Override
//    public String toString() {
//        return "CovidCountBean{" +
//                "cases=" + cases +
//                ", deaths=" + deaths +
//                '}';
//    }

    //修改一下,返回的都是数据
    @Override
    public String toString() {
        return cases + "\t" + deaths;
    }


    /**
     * 序列化方法,可以控制把哪写字段序列化出去
     */
    @Override
    public void write(DataOutput dataOutput) throws IOException {
        dataOutput.writeLong(cases);
        dataOutput.writeLong(deaths);
    }

    /**
     *  反序列化方法
     *  todo 注意反序列化的顺序和序列化顺序一致
     */
    @Override
    public void readFields(DataInput dataInput) throws IOException {
        this.cases = dataInput.readLong();
        this.deaths = dataInput.readLong();
    }

    /**
     *自定义对象的排序方法
     * 返回结果: 0 相等, 负数 小于, 正数 大于
     * 倒序精髓:如果大于,强制返回负数; 如果小于,强制返回正数
     */
    @Override
    public int compareTo(CovidCountBean2 o) {
        return this.cases - o.getCases() > 0 ? -1 : (this.cases - o.getCases() < 0 ? 1 : 0) ;
    }
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值