Hadoop的MapReduce如何自定义排序

Hadoop的MR自定义排序其实很简单,其本质就是扩展了自定义Bean的Key,比如下面这个例子,我希望完成订单价格的排序

在正式进入代码编程之前,我先要给大家介绍两个接口,一个是WritableComparable,另外一个是Writable,这两个接口啊,就是给我们提供的,让我们自定义数据类型的,他们两个的区别就在于Writable,不能用来自定义key,因为我在MR的原理中说过了,MR在运行的时候会将数据进行排序,且是以k为核心,所以自定义key的时候必须需要其中有排序的方法
至于为什么不去使用JAVA提供的排序接口,这一点我在MR原理中已经说了,这已经不在多说了,想了解的可以翻翻我其他的博文
而现在啊,我要给大家说的是MR之所以能够用key实现自定义排序,这和它本身的设计有关系,MR是磁盘计算,在可操作度上我们可以理解为他每一次实现的操作只相当于spark的一个算子,所以自定义排序,的代码,大家会发现一个有意思的事情,就是我们通常写自定义排序代码之后,都是按照key去排序,而value上不留任何东西,因为无意义,我们操作自定义排序计算的时候,其实就是相当于舍弃掉了组内成员的操作意义,只为了让不同的组之间排序,因为MR的底层就是用排序去区分组,因此我们现在将它的意义原始化,不再考虑MR排序等价分组,而是直接使用它的排序意义让组合组之间排序
有人会有疑问,既然我们,舍弃组内成员的操作意义,那为什么reducer还有循环?有这个疑问的,我只能说你考虑问题不够全面,要知道我们现在只是在我们操作的意义上舍弃了组内的操作,但是其实MR还是存在分组意义,因此还是会发生数据在同一组的情况,所以我们才遍历了数据为空的集合
至于为什么我们动的空集还能操作不同的key,有这个疑问的人,那么你对MR的原理应该去多想一想,多想一想你就会发现,其实MR只是完成了分组,并没有把相同的key和为一个,而且我们作为程序员,应该知道写代码的五大设计要求,其中有一个要求,我们在写代码的时候只填代码,哪怕一个东西淘汰了,我们最多只是标记不再使用,不到万不得已下不删代码,我大胆猜测,MR也是这个样子,他有这样自定义排序需求的时候,他如果在对组内进行一次排序,那么这个计算量度与本身就体系非常庞大的MR来说就太累赘了,但是本身设计又有限制,所以他以一个key的形式向我们展示可操作的数据,但是其实它内部在运行时,Key和我们在便利的那个value,其实指向的是一对数据,所以key随着变化而变化

package com.wy;

import org.apache.hadoop.io.WritableComparable;

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

public class OrderBean implements WritableComparable<OrderBean>{

    private  int orderId;
    private  double price;

    public OrderBean() {
    }

    public void setOrderBean(int orderId, double price) {
        this.orderId = orderId;
        this.price = price;
    }

    public int getOrderId() {
        return orderId;
    }

    public void setOrderId(int orderId) {
        this.orderId = orderId;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

	/**
	* 我写的是二级排序,大家可以根据自己的需要书写排序
	*/
    @Override
    public int compareTo(OrderBean o) {
        int price=Double.compare(o.price,this.price);
        int id = Integer.compare(o.orderId, this.orderId);
        if ( id == 0 ){
            return  price;
        }else {
            return id;
        }

    }

    @Override
    public void write(DataOutput dataOutput) throws IOException {
        dataOutput.writeInt(this.orderId);
        dataOutput.writeDouble(this.price);
    }

    @Override
    public void readFields(DataInput dataInput) throws IOException {
        this.orderId=dataInput.readInt();
        this.price=dataInput.readDouble();
    }

    @Override
    public String toString() {
        return orderId +
                "\t" + price ;
    }
}

大家仔细看这个类,其实就会发现,就像我说的那样,就是扩展了compareTo方法,但是我们要注意书写的排序在key上才会生效,因为MapReduce的底层在分组排序的时候是以key为核心的,同时我们书写的时候也要注意逻辑,因为排序的级别越高,那么就可能导致本来在一个组的数据,在洗牌之后变成了不同的组,而使用这个类的时候也不需要另外用job对象做设置照常写就好

    public static void main(String[] args) throws InterruptedException, IOException, ClassNotFoundException {
        Configuration cfg = new Configuration();
        //获取到任务
        Job job = Job.getInstance(cfg);
        job.setJarByClass(OrderDirver.class);
        //对输入输出参数设置
        job.setOutputKeyClass(OrderBean.class);
        job.setOutputValueClass(NullWritable.class);

        job.setMapOutputKeyClass(OrderBean.class);
        job.setMapOutputValueClass(NullWritable.class);
        //设置map reduce类
        job.setMapperClass(Map.class);
        job.setReducerClass(Reduce.class);

        //设置输入输出路径
        FileInputFormat.setInputPaths( job , new Path("D:\\a\\input"));
        FileOutputFormat.setOutputPath( job , new Path("D:\\a\\output"));
        boolean b = job.waitForCompletion(true);
        System.exit(b == true ? 0 : -1);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值