本文发表于本人博客

今天继续写练习题,上次对分区稍微理解了一下,那根据那个步骤分区、排序、分组、规约来的话,今天应该是要写个排序有关的例子了,那好现在就开始!

说到排序我们可以查看下hadoop源码里面的WordCount例子中对LongWritable类型定义,它实现抽象接口WritableComparable,代码如下:

public interface WritableComparable<T> extends Writable, Comparable<T> {
}
public interface Writable {
void write(DataOutput out) throws IOException;
void readFields(DataInput in) throws IOException;
}

其中Writable抽象接口定义了write以及readFields方法,分别是写入数据流以及读取数据流。而Comparable中又有compareTo方法定义比较。竟然hadoop的内置类型有比较大小功能,那么它使用这个内置类型作为map端输出的话是怎么样去排序的,这个问题我们先来查看下map任务类MapTask源代码,内部有内置MapOutputBuffer类,在spill accounting注释下面有个排序字段:

private final IndexedSorter sorter;

这个字段是由:

sorter = ReflectionUtils.newInstance(job.getClass("map.sort.class", QuickSort.class, IndexedSorter.class), job);

可以看出,这个排序算法可以在配置文件中指定,不过默认是快速排序QuickSort。这个QuickSort内部有几个重要的方法:

public void sort(final IndexedSortable s, int p, int r,final Progressable rep);
private static void sortInternal(final IndexedSortable s, int p, int r,final Progressable rep, int depth);

其中在传递参数IndexSortable的时候是用MapOutputBuffer当前来传递,因为这个MapOutputBuffer也继承IndexedSortable.这样在QuickSort排序sort中就会使用MapOutputBuffer类中的compare方法进行比较,可以看下面源代码:

    public int compare(int i, int j) {
final int ii = kvoffsets[i % kvoffsets.length];
final int ij = kvoffsets[j % kvoffsets.length];
// sort by partition
if (kvindices[ii + PARTITION] != kvindices[ij + PARTITION]) {
return kvindices[ii + PARTITION] - kvindices[ij + PARTITION];
}
// sort by key
return comparator.compare(kvbuffer,
kvindices[ii + KEYSTART],
kvindices[ii + VALSTART] - kvindices[ii + KEYSTART],
kvbuffer,
kvindices[ij + KEYSTART],
kvindices[ij + VALSTART] - kvindices[ij + KEYSTART]);
}

然而这个方法中comparator默认是由节点“mapred.output.key.comparator.class”决定,也可以看源码:

  public RawComparator getOutputKeyComparator() {
Class<? extends RawComparator> theClass = getClass("mapred.output.key.comparator.class",
null, RawComparator.class);
if (theClass != null)
return ReflectionUtils.newInstance(theClass, this);
return WritableComparator.get(getMapOutputKeyClass().asSubclass(WritableComparable.class));
}

就是这样把排序以及比较方法关联起来了!那现在我们可以按照LongWritable的思路实现自己的自定义类型并且读取、写入、比较。下面写写代码加深下记忆,既然是排序那我们准备下数据,如下有2列数据要求按照第一列升序,第二列降序排序:

1    2
1 1
3 0
3 2
2 2
1 2

先自定义类型SortAPI:

public class SortAPI implements WritableComparable<SortAPI> {
/**
* 第一列数据
*/
public Long first;
/**
* 第二列数据
*/
public Long second; public SortAPI(){}
public SortAPI(long first,long second){
this.first = first;
this.second = second;
}
/**
* 排序就在这里当:this.first - o.first > 0 升序,小于0倒序
*/
@Override
public int compareTo(SortAPI o) {
long mis = (this.first - o.first);
if(mis != 0 ){
return (int)mis;
}
else{
return (int)(this.second - o.second);
}
} @Override
public void write(DataOutput out) throws IOException {
out.writeLong(first);
out.writeLong(second);
} @Override
public void readFields(DataInput in) throws IOException {
this.first = in.readLong();
this.second = in.readLong(); } @Override
public int hashCode() {
return this.first.hashCode() + this.second.hashCode();
} @Override
public boolean equals(Object obj) {
if(obj instanceof SortAPI){
SortAPI o = (SortAPI)obj;
return this.first == o.first && this.second == o.second;
}
return false;
}
@Override
public String toString() {
return "first:" + this.first + "second:" + this.second;
}
}

这类型重写compareTo(SortAPI o)、write(DataOutput out)、readFields(DataInput in),既然是有比较那么以前说的就一定要重写hashCode()、equals(Object obj)方法了,这点不要忘记!还需要主要在write方法以及readFields方法中读写是有顺序:先write什么字段就先read什么字段。其次这个compareTo(SortAPI o)方法中返回是整型大于0、0、以及小于0代表大于、等于、小于。至于怎么判断2行数据是不是相等,不相等怎么比较着逻辑可以慢慢看下。

下面写个自定义Mapper、Reducer类以及main函数:

public class MyMapper extends Mapper<LongWritable, Text, SortAPI, LongWritable> {

    @Override
protected void map(LongWritable key, Text value,Context context) throws IOException, InterruptedException {
String[] splied = value.toString().split("\t");
try {
long first = Long.parseLong(splied[0]);
long second = Long.parseLong(splied[1]);
context.write(new SortAPI(first,second), new LongWritable(1));
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
public class MyReduce extends Reducer<SortAPI, LongWritable, LongWritable, LongWritable> {

    @Override
protected void reduce(SortAPI key, Iterable<LongWritable> values, Context context) throws IOException, InterruptedException {
context.write(new LongWritable(key.first), new LongWritable(key.second));
} }
    static final String OUTPUT_DIR = "hdfs://hadoop-master:9000/sort/output/";
static final String INPUT_DIR = "hdfs://hadoop-master:9000/sort/input/test.txt"; public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = new Job(conf, Test.class.getSimpleName());
deleteOutputFile(OUTPUT_DIR); //1设置输入目录
FileInputFormat.setInputPaths(job, INPUT_DIR);
//2设置输入格式化类
job.setInputFormatClass(TextInputFormat.class);
//3设置自定义Mapper以及键值类型
job.setMapperClass(MyMapper.class);
job.setMapOutputKeyClass(SortAPI.class);
job.setMapOutputValueClass(LongWritable.class);
//4分区
job.setPartitionerClass(HashPartitioner.class);
job.setNumReduceTasks(1); //5排序分组
//6设置在一定Reduce以及键值类型
job.setReducerClass(MyReduce.class);
job.setOutputKeyClass(LongWritable.class);
job.setOutputValueClass(LongWritable.class);
//7设置输出目录
FileOutputFormat.setOutputPath(job, new Path(OUTPUT_DIR));
//8提交job
job.waitForCompletion(true);
} static void deleteOutputFile(String path) throws Exception{
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(new URI(INPUT_DIR),conf);
if(fs.exists(new Path(path))){
fs.delete(new Path(path));
}
}

这样在eclipse下就可以直接运行查看结果:

1       1
1 2
2 2
3 0
3 2

这结果正确,那如果要求第一列倒叙第二列升序呢,怎么办,这只需要修改下compareTo(SortAPI o):

    @Override
public int compareTo(SortAPI o) {
long mis = (this.first - o.first) * -1 ;
if(mis != 0 ){
return (int)mis;
}
else{
return (int)(this.second - o.second);
}
}

这样保存在运行,结果:

3       0
3 2
2 2
1 1
1 2

也正确吧符合自己的这个要求。

留个小问题:这个compareTo(SortAPI o)方法在什么时候调用了,总共调用了几次?

这次先到这里。坚持记录点点滴滴!

Hadoop mapreduce自定义排序WritableComparable的更多相关文章

  1. Hadoop mapreduce自定义分组RawComparator

    本文发表于本人博客. 今天接着上次[Hadoop mapreduce自定义排序WritableComparable]文章写,按照顺序那么这次应该是讲解自定义分组如何实现,关于操作顺序在这里不多说了,需 ...

  2. 2 weekend110的hadoop的自定义排序实现 + mr程序中自定义分组的实现

    我想得到按流量来排序,而且还是倒序,怎么达到实现呢? 达到下面这种效果, 默认是根据key来排, 我想根据value里的某个排, 解决思路:将value里的某个,放到key里去,然后来排 下面,开始w ...

  3. Hadoop学习之路(7)MapReduce自定义排序

    本文测试文本: tom 20 8000 nancy 22 8000 ketty 22 9000 stone 19 10000 green 19 11000 white 39 29000 socrate ...

  4. mapreduce自定义排序(map端1.4步)

    3 3 3 2 3 1 2 2 2 1 1 1 -----------------期望输出 1 1 2 1 2 2 3 1 3 2 3 3 将以上数据进行排序,排序规则是:按照第一列升序排序,如果第一 ...

  5. hadoop MapReduce辅助排序解析

    1.数据样本,w1.csv到w5.csv,每个文件数据样本2000条,第一列是年份从1990到2000随机,第二列数据从1-100随机,本例辅助排序目标是找出每年最大值,实际上结果每年最大就是100, ...

  6. 三种方法实现Hadoop(MapReduce)全局排序(1)

    我们可能会有些需求要求MapReduce的输出全局有序,这里说的有序是指Key全局有序.但是我们知道,MapReduce默认只是保证同一个分区内的Key是有序的,但是不保证全局有序.基于此,本文提供三 ...

  7. Hadoop mapreduce自定义分区HashPartitioner

    本文发表于本人博客. 在上一篇文章我写了个简单的WordCount程序,也大致了解了下关于mapreduce运行原来,其中说到还可以自定义分区.排序.分组这些,那今天我就接上一次的代码继续完善实现自定 ...

  8. [Hadoop] - Mapreduce自定义Counter

    在Hadoop的MR程序开发中,经常需要统计一些map/reduce的运行状态信息,这个时候我们可以通过自定义Counter来实现,这个实现的方式是不是通过配置信息完成的,而是通过代码运行时检查完成的 ...

  9. 【Hadoop】Hadoop MR 自定义排序

    1.概念 2.代码示例 FlowSort package com.ares.hadoop.mr.flowsort; import java.io.IOException; import org.apa ...

随机推荐

  1. HTML&CSS精选笔记_CSS高级技巧

    CSS高级技巧 CSS精灵技术 需求分析 CSS精灵是一种处理网页背景图像的方式.它将一个页面涉及到的所有零星背景图像都集中到一张大图中去,然后将大图应用于网页,这样,当用户访问该页面时,只需向服务发 ...

  2. Java精选笔记_网络编程

    网络编程 概述 现在的网络编程基本上都是基于请求/响应方式的,也就是一个设备发送请求数据给另外一个,然后接收另一个设备的反馈. 在网络编程中,发起连接程序,也就是发送第一次请求的程序,被称作客户端(C ...

  3. X-code 描述文件的位置

    不管是真机测试还是打包的过程中,都需要描述文件.在桌面上,按快捷键“commd+Shift+G”,就会显示一个要填的文件路径,如下图: 找到描述文件的路径: ~/Library/MobileDevic ...

  4. lower()

    lower() 用于把字符串中的大写字母转换成小写字母 In [1]: str = "Hello World" In [2]: str.lower() Out[2]: 'hello ...

  5. linux系统usb挂载

    本次例程的环境是在FC6下,通过终端操作的. 注意要挂载U盘需要有管理员的权限. 切换成管理员,输入: su root 然后输入管理员密码,进行密码认证: 成功后,先在 /mnt 下建立一个名叫USB ...

  6. 《C++ Primer Plus》14.3 多重继承 学习笔记

    多重继承(MI)描述的是有多个直接基类的类.与单继承一样,共有MI表示的也是is-a关系.例如,可以从Awiter类和Singer类派生出SingingWaiter类:class SingingWai ...

  7. MYSQL系列之(二)

    上一篇文章讲的是mysql的基本操作,这一篇会有一点难以理解,本节主要内容mysql视图,存储过程,函数,事务,触发器,以及动态执行sql 视图view 视图是一个虚拟表,其内容由查询定义.同真实的表 ...

  8. centos 7 搭建ntp 服务器

    第一步 安装ntp yum install ntp 第二步,查找最近的时间同步服务器 http://www.pool.ntp.org/zone/asia 第三部编辑 /etc/ntp.conf ser ...

  9. Fragments (官方文档中文版)

    转 http://blog.sina.com.cn/s/blog_69a4fbd70100r5j4.html   概述   Fragment表现Activity中UI的一个行为或者一部分.可以将多个f ...

  10. Sql Server 关于列名带中括号"[]"的问题

    1.如果列名为数据库的关键字则自动加上中括号“[]” 例如[level] 2.如果列名中带有特殊符号.[date(a)] 数据存储的过程: 1.在添加数据的时候:要带有中括号,有必要在添加参数的时候不 ...