Hadoop mapreduce中自定义排序及自定义分组

一、map自定义排序

哪个字段需要排序,将其设为map输出的key,利用map的排序完成。

  1. 如果字段为基本类型且正序排序,则直接设为key,利用map默认排序即可。
  2. 如果字段为对象或需要倒序排序,则需利用对象类实现comparable(WritableComparable)接口,重写接口的comparable方法。

二、map自定义分组

需新建分组类,继承WritableComparator类,重写compare方法,返回0则归为一组。
main方法中需指定分组类。

三、举例

现有订单及对应单价的原始数据,每笔订单有不同产品对应不同单价,要求MapReduce最终输出每笔订单的最大单价。

订单号产品号价格
0001prod1200
0001prod2300
0001prod3400
0002prod2350
0002prod3450
0003prod4800
0003prod51500
0003prod61200
0004prod61200

代码如下:

1.对象类Order

用以封装订单号及价格。

  1. 建立无参、满参构造方法,set/get方法,重写toString方法
  2. 继承writableCompareble接口,重写compareTo/write/readFields方法
  3. compareTo方法:返回1则指定的数大于参数;-1则小于参数;0则等于参数。
  4. write方法序列化方法
  5. readFields反序列化方法
import org.apache.hadoop.io.WritableComparable;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

public class Order implements WritableComparable<Order> {
   private int orderId;
   private double price;

   //set/get/构造方法省略
   
   //重写toString方法
   @Override
   public String toString() {
       return orderId+"\t"+price;
   }
   
   //先对OrderID排序,正序倒序无所谓,当OrderID相同的时候,对价格排序,倒序,即当this.getPrice() > o.getPrice() 时,返回 -1则认为是倒序,1则是正序。
   public int compareTo(Order o) {
       return this.getOrderId() > o.getOrderId() ? 1 : (this.getOrderId() < o.getOrderId() ? -1 : (this.getPrice() > o.getPrice() ? -1 : 1));
   }

   //hadoop参数传递的序列化方法
   public void write(DataOutput dataOutput) throws IOException {
       dataOutput.writeInt(orderId);
       dataOutput.writeDouble(price);
   }

   //反序列化方法
   public void readFields(DataInput dataInput) throws IOException {
       this.orderId=dataInput.readInt();
       this.price=dataInput.readDouble();
   }
}

2.分组类 OrderGrouping

新建类继承WritableComparator类,重写compare方法,返回值为0时,归为一组。

import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;

public class OrderGrouping extends WritableComparator {
	//固定套路,允许通过反射创建实例
    protected OrderGrouping(){
        super(Order.class,true);
    }

    @Override
    public int compare(WritableComparable a, WritableComparable b) {
    	//类型强转为Order
        Order abean=(Order) a;
        Order bbean=(Order) b;
        //按OrderID分组,当两个OrderID相同时,返回0,则被认为是同一组;
        return abean.getOrderId()-bbean.getOrderId();
    }
}

3.Mapper类LargestOrderMapper

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

public class LargestOrderMapper extends Mapper<LongWritable,Text,Order,NullWritable> {
    IntWritable v = new IntWritable();
    Order k = new Order();

	//获取数据,封装到Order对象中,传递给Reduce
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String[] split = value.toString().split("\t");
            k.setOrderId(Integer.parseInt(split[0]));
            k.setPrice(Double.parseDouble(split[2]));
            context.write(k,NullWritable.get());
    }
}

4.Reducer类LargestOrderReducer

reduce输出的key,随着每组iterable中的迭代而迭代,并非固定的key。
若想输出的key是每组中的第一个key,则直接输出无需迭代;
若想输出每组最后一个key,则迭代后取值即可。

import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;

public class LargestOrderReducer extends Reducer<Order,NullWritable,Order,NullWritable> {

    @Override
    protected void reduce(Order key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
    	//因为在同一分组内price倒序,因此直接取每组第一个order类,即为该组的price最大值
        context.write(key,NullWritable.get());
    }
}

5.Main方法所在类LargestOrderDriver

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
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.IOException;

public class LargestOrderDriver {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        Configuration conf = new Configuration();

        //设定本地执行
        conf.set("mapreduce.framework.name", "local");

        Job job = Job.getInstance(conf);
        Job.getInstance();

        //指定我这个 job 所在的 jar包位置
        job.setJarByClass(LargestOrderDriver.class);

        //指定map分组的类
        job.setGroupingComparatorClass(OrderGrouping.class);

        //指定我们使用的Mapper是那个类  reducer是哪个类
        job.setMapperClass(LargestOrderMapper.class);
        job.setReducerClass(LargestOrderReducer.class);

        // 设置我们的业务逻辑 Mapper 类的输出 key 和 value 的数据类型
        job.setMapOutputKeyClass(Order.class);
        job.setMapOutputValueClass(NullWritable.class);

        // 设置我们的业务逻辑 Reducer 类的输出 key 和 value 的数据类型
        job.setOutputKeyClass(Order.class);
        job.setOutputValueClass(NullWritable.class);

		// 指定数据位置
        FileInputFormat.setInputPaths(job, new Path("F:\\IT\\input\\新建文本文档.txt"));
        // 指定处理完成之后的结果所保存的位置
        FileOutputFormat.setOutputPath(job, new Path("F:\\IT\\output1"));

        // 向 yarn 集群提交这个 job
        boolean res = job.waitForCompletion(true);
        System.exit(res ? 0 : 1);
    }
}

四、拓展 需求更进一步

相对于取price最大值,如何取排前N项的price(比如2?)
总体思路:
排序及分组无需改变,只需在reduce时,相对于直接输出最大值,改为迭代,if判断迭代到的是第几个即可。符合条件的进行输出。
因此之前传递的value不能取NullWritable,而取一个有意义的值,比如price,以便可以迭代。
修改mapper及reducer如下:
mapper
传递值改为DoubleWritable

public class LargestOrderTopNMapper extends Mapper<LongWritable,Text,Order,DoubleWritable> {
    Order k = new Order();
    DoubleWritable v = new DoubleWritable();

    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        String[] split = value.toString().split("\t");
        k.setOrderId(Integer.parseInt(split[0]));
        k.setPrice(Double.parseDouble(split[2]));
        v.set(Double.parseDouble(split[2]));
        context.write(k,v);
    }
}

Reducer
接收值改为DoubleWritable
增加输出判断条件

public class LargestOrderTopNReducer extends Reducer<Order, DoubleWritable, Order, NullWritable> {

    @Override
    protected void reduce(Order key, Iterable<DoubleWritable> values, Context context) throws IOException, InterruptedException {
        int count = 0;
        for (DoubleWritable value : values) {
        	//排名前二输出
            if (count <= 1) {
                context.write(key, NullWritable);
            }
            count++;
        }
    }
}

相应修改Driver类中传递的参数类型。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值