mapreduce求每个订单中的价格从高到低的x行数据

package day12_3;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.*;
import org.apache.hadoop.mapred.lib.HashPartitioner;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Partitioner;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.junit.Test;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

//求每个订单中的最大单价的那行数据
//         订单编号      商品编号 价格
//        Order_0000001,Pdt_01,222.8
//        Order_0000001,Pdt_05,25.8
//        Order_0000002,Pdt_05,325.8
//        Order_0000002,Pdt_03,522.8
//        Order_0000002,Pdt_04,122.4
//        Order_0000003,Pdt_01,222.8
//思路:将订单编号,商品编号,价格封装成类
//实现该类的WritableComparable接口,使对象可以在MR中使用--重写序列化方法write,反序列化方法read()
//按订单编号(升序)和价格(降序)排序
//为什么要分区和分组?---要产生不同的文件。
// 要是按照默认hashcode分组的话,每个对象的hashcode都不一样,没办法让订单编号相同的
//被分到一个组---即被分到一个文件里
//分组---因为在reduce()方法时,要值相同的一组键掉用一次reduce()方法,因为键是多个不同的对象,会被认为是一个对象是单独的一组
//所以要写成符合我们条件的样子:在订单编号相同的情况下被认为是一组

//总结:
//所以要想生成三个文件,必须分区--这里让ID号相同的在一个区,用getPartition()方法实现
//必须实现分组,不然没办法在调用reduce()时,订单号相同的OrderBean对象被放进同一个迭代器
//为什么要把订单编号相同对象放进一个组??放进一个组是想用迭代器,拿到相同id的一组OrderBean对象,
//从而得到topN(topN是指倒序排序,取最上面的N个数据),
//要想对象序列化,且根据某属性有序,必须实现WritableComparable接口--这里根据D订单编号和价格排序
//键和值都存OrderBean是因为只有值能进行迭代,键已经是我们想要的结果,但是键没办法通过迭代器取出,
//所以把相同的键在值放一份,方便取出
//分区排序分组的顺序?
//写进缓冲区的80%数据,先反序列化,进行分区,再排序(从而达到分区内有序的效果),接着溢出(写到磁盘成为二进制的字符串),
//之后这些二进制字符串两两Merge(这里用归并排序,归并排序只让部分数据写进内存,也就是让部分二进制的数据反序列化为对象),
// 每个MapTask输出一个二进制文件,当一个这种文件被合并完,就开始启动Reduce端,然后在ReduceTask里会将所有的MapTask传来的
//二进制串最终合并成一个大文件,然后会把这个大文件按照它的索引,把不同区的数据放到它所对应的分区,之后调用reduce方法,
//对同组的数据进行迭代,这时候才涉及到分组。
public class Price {

    //序列化且有序的类
    public static class OrderBean implements WritableComparable<OrderBean>
    {
        private String orderId;
        private String itemId;
        private double price;

        public String getOrderId() {
            return orderId;
        }

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

        public String getItemId() {
            return itemId;
        }

        public void setItemId(String itemId) {
            this.itemId = itemId;
        }

        public double getPrice() {
            return price;
        }

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

        public OrderBean(){}
        public void set(String orderId,String itemId,double price)
        {
            this.orderId=orderId;
            this.itemId=itemId;
            this.price=price;
        }
        //只要涉及实现Writable接口,将对象序列化和反序列话
        //就必须写该类的无参构造方法,以为反序列化的时候先调用无参构造方法
        //没有就会报错
        @Override
        public String toString() {
            return orderId+"\t"+itemId+"\t"+price;
        }

        @Override
        public int compareTo(OrderBean o) {
            int a=this.orderId.compareTo(o.orderId);
            int tmp=this.price-o.price>0? -1 :1;//价格降序
            return a==0?tmp:a;
        }

        @Override//序列化方法
        public void write(DataOutput out) throws IOException {
            out.writeUTF(orderId);
            out.writeUTF(itemId);
            out.writeDouble(price);
        }

        @Override//反序列化方法
        public void readFields(DataInput in) throws IOException {
            orderId=in.readUTF();
            itemId=in.readUTF();
            price=in.readDouble();
        }
    }
    //自定义Partirioner(分区器)
    //默认的分区器为HashPartitioner,可以点进去看源码
    public static class OrderPartitioner extends Partitioner<OrderBean,OrderBean>
    {
        @Override
        //将orderId相同的OrederBean对象分在一个区,发往相同的reduceTask,生成一个文件
        //默认的为HashPartitioner
        public int getPartition(OrderBean key, OrderBean value, int numPartitions)
        {
            //&Integer.MAX_VALUE是因为源码是这么写的
            return (key.getOrderId().hashCode()&Integer.MAX_VALUE)% numPartitions;
        }

    }

    //自定义分组
    //WritableComparator和WritableComparable的区别
    //从单词意思上看Comparable是可比较的,Comparator是比较器
    //WritableComparator只是单纯的一个比较器而已,它没有write()和read()方法
    //WritableComparable是指可以让类的实例化对象可以进行比较
    //为什么要用比较器?为什么不用默认的比较器(调用compareTo方法)
    //因为这个题中我们写的compareTo方法只有当订单编号和价格一样才会返回0,
    //而分组的话,是把订单编号相同认为是分成一个组,这就要求在订单编号相同的时候返回0
    //所以要重写一个
    public static  class OrderGroupingComparator extends WritableComparator
    {

        public OrderGroupingComparator() {
            //true表示创建一个构造器的实例
            super(OrderBean.class,true);
        }

        @Override
        public int compare(WritableComparable a, WritableComparable b) {
            OrderBean abean=(OrderBean)a;
            OrderBean bbean=(OrderBean)b;
            //方法的类型为WritableComparable,这个方法的实参是OrderBean
            //OrderBean implements WritableComparable
            //所以实参赋值给形参相当于子类的对象给父类的引用赋值(多态)
            //所以这个父类对象可以转回子类(这样转的条件是多态,这里已经满足了)
            String aBeanOrderId=abean.getOrderId();
            String bBeanOrderId=bbean.getOrderId();
            return aBeanOrderId.compareTo(bBeanOrderId);
        }
    }


    //为什么键和值都存OrderBean?
    //因为要输出TopN,这是通过迭代器输出的,而迭代器是针对值的
    public static class MyMapper extends Mapper<LongWritable, Text,OrderBean,OrderBean>
    {
        //定义数据成员
        private static OrderBean k=new OrderBean();

        //在调用map()方法之前被自动调用,且只调用一次,相当于初始化
        @Override
        protected void setup(Context context) throws IOException, InterruptedException {
            super.setup(context);
        }

        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            String line=value.toString();
            String[] fields=line.split(",");
            k.set(fields[0],fields[1],Double.parseDouble(fields[2]));
            context.write(k,k);
        }

        //在mao()调用结束后被自动调用,且只调用一次,相当于垃圾回收
        @Override
        protected void cleanup(Context context) throws IOException, InterruptedException {
            super.cleanup(context);
        }
    }
    public static class MyReducer extends Reducer<OrderBean,OrderBean,OrderBean, NullWritable>
    {
        private static int topn;

        //初始化
        @Override
        protected void setup(Context context) throws IOException, InterruptedException {
            topn=Integer.parseInt(context.getConfiguration().get("topn"));
        }

        @Override
        protected void reduce(OrderBean key, Iterable<OrderBean> values, Context context) throws IOException, InterruptedException {
            int count=0;
            for(OrderBean bean:values)
            {
                context.write(bean,NullWritable.get());
                count++;
                if(count==topn)
                    return;
            }
        }

        @Override
        protected void cleanup(Context context) throws IOException, InterruptedException {
            super.cleanup(context);
        }
    }
    public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
        Configuration conf=new Configuration();
        conf.set("topn",args[0]);//动态获得topn的值
        System.setProperty("HADOOP_USER_NAME", "root");
        conf.set("fs.defaultFS","file:///");//在本地运行
        conf.set("mapreduce.framework.name","local");
        Job job=Job.getInstance(conf,"price");
        job.setPartitionerClass(OrderPartitioner.class);//指定分区
        job.setGroupingComparatorClass(OrderGroupingComparator.class);//指定分组
        job.setNumReduceTasks(3);//设置reduceTask的个数
        job.setJarByClass(Price.class);//设置jar包的位置,main方法在哪jar包就在哪
        job.setMapperClass(MyMapper.class);
        job.setReducerClass(MyReducer.class);
        //设置Map端输出的key,value类型
        job.setMapOutputKeyClass(OrderBean.class);
        job.setMapOutputValueClass(OrderBean.class);
        //设置Reduce端输出(也就是最终输出)的key,value的类型
        job.setOutputKeyClass(OrderBean.class);
        job.setOutputValueClass(NullWritable.class);
        FileInputFormat.setInputPaths(job,new Path("D:\\mrText\\price\\input"));
        FileOutputFormat.setOutputPath(job,new Path("D:\\mrText\\price\\output"));
        boolean b=job.waitForCompletion(true);
        System.exit(b?0:1);
    }
//    @Test
//    public void Text(){
//        System.out.println(NullWritable.get()+" "+NullWritable.get());
//        System.out.println("haha");
//    }

}

输出:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值