RawComparator

hadoop的原生比较器RawComparator<T> public WritableCom...

1人收藏此文章,我要收藏发表于11个月前(2012-02-08 16:24) , 已有 312次阅读 ,共 0个评论

hadoop为序列化提供了优化,类型的比较对M/R而言至关重要,Key和Key的比较也是在排序阶段完成的,hadoop提供了原生的比较器接口RawComparator<T>用于序列化字节间的比较,该接口允许其实现直接比较数据流中的记录,无需反序列化为对象,RawComparator是一个原生的优化接口类,它只是简单的提供了用于数据流中简单的数据对比方法,从而提供优化:

1 publicinterfaceRawComparator<T>extendsComparator<T> {
2
3 publicintcompare(byte[] b1,ints1,intl1,byte[] b2,ints2,intl2);
4
5 }

该类并非被多数的衍生类所实现,其具体的子类为WritableComparator,多数情况下是作为实现Writable接口的类的内置类,提供序列化字节的比较。下面是RawComparator接口内置类的实现类图:

首先,我们看RawComparator的具体实现类WritableComparator:


WritableComparator类类似于一个注册表,里面记录了所有Comparator类的集合。

Comparators成员用一张Hash表记录Key=Classvalue=WritableComprator的注册信息.

WritableComparator主要提供了两个功能

1.提供了对原始compare()方法的一个默认实现

默认实现是先反序列化为对像再通过对像比较(有开销的问题)

public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {

try {

buffer.reset(b1, s1, l1);// parse key1

key1.readFields(buffer);

buffer.reset(b2, s2, l2);// parse key2

key2.readFields(buffer);

} catch (IOException e) {

throw new RuntimeException(e);

}

return compare(key1, key2);// compare them

}

而对应的基础数据类型的compare()的实现却巧妙的利用了特定类型的泛化:(利用了writableComparablecompareTo方法)

public int compare(WritableComparable a, WritableComparable b) {

return a.compareTo(b);

}

例如IntWritable实例是调用了IntWritable里的compareTo方法

public int compareTo(Object o) {

int thisValue = this.value;

int thatValue = ((IntWritable)o).value;

return (thisValue<thatValue ? -1 : (thisValue==thatValue ? 0 : 1));

}

2.充当RawComparable实例的工厂,以注册Writable的实现

例如,为了获取IntWritableComparator,可以直接调用其get方法。

WritableComparator

关键代码:

代码1registry注册器

----------------------------------------------------------------

// registry注册器:记载了WritableComparator类的集合

privatestaticHashMap<Class,WritableComparator>comparators=

newHashMap<Class, WritableComparator>();

代码2获取WritableComparator实例

说明:hashMap作为容器类线程不安全,故需要synchronized同步,get方法根据key=Class返回对应的WritableComparator,若返回的是空值NUll,则调用protected Constructor进行构造,而其两个protected的构造函数实则是调用了newKey()方法进行NewInstance

1 publicstaticsynchronizedWritableComparator get(Class<?extendsWritableComparable> c) {
2 WritableComparator comparator = comparators.get(c);
3 if(comparator ==null)
4 comparator =newWritableComparator(c,true);
5 returncomparator;
6 }

代码3构造方法

---------------------------------------------------------------

01 newWritableComparator(c,true)
02
03 WritableComparator的构造函数源码如下:
04
05 /*
06
07 * keyClass,key1,key2和buffer都是用于WritableComparator的构造函数
08
09 */
10
11 privatefinalClass<?extendsWritableComparable> keyClass;
12
13 privatefinalWritableComparable key1;//WritableComparable接口
14
15 privatefinalWritableComparable key2;
16
17 privatefinalDataInputBuffer buffer;//输入缓冲流
18
19 protectedWritableComparator(Class<?extendsWritableComparable> keyClass,
20
21 booleancreateInstances) {
22
23 this.keyClass = keyClass;
24
25 if(createInstances) {
26
27 key1 = newKey();
28
29 key2 = newKey();
30
31 buffer =newDataInputBuffer();
32
33 }else{
34
35 key1 = key2 =null;
36
37 buffer =null;
38
39 }
40
41 }

上述的keyClasskey1,key2,buffer是记录HashMap对应的key值,用于WritableComparator的构造函数,但由其构造函数中我们可以看出WritableComparator根据Boolean createInstance来判断是否实例化key1,key2buffer,key1,key2作为实现了WritableComparable接口的标识,在WritableComparator的构造函数里面通过newKey()的方法去实例化实现WritableComparable接口的一个对象,下面是newKey()的源码,通过hadoop自身的反射去实例化了一个WritableComparable接口对象。

1 <b>publicWritableComparable newKey() {
2
3 returnReflectionUtils.newInstance(keyClass,null);
4
5 }
6
7 </b>

代码4Compare()方法

---------------------------------------------------------------------

1.publicintcompare(Object a, Object b)

2.publicintcompare(WritableComparablea,WritableComparableb)

3.publicintcompare(byte[] b1,ints1,intl1,byte[] b2,ints2,intl2)

三个compare()重载方法中,compare(Object a, Object b)利用子类塑形为WritableComparable而调用了第2compare方法,而第2Compare()方法则调用了Writable.compaerTo();最后一个compare(byte[] b1,ints1,intl1,byte[] b2,ints2,intl2)方法源码如下:

01 publicintcompare(byte[] b1,ints1,intl1,byte[] b2,ints2,intl2) {
02
03 try{
04
05 buffer.reset(b1, s1, l1);// parse key1
06
07 key1.readFields(buffer);
08
09
10
11 buffer.reset(b2, s2, l2);// parse key2
12
13 key2.readFields(buffer);
14
15
16
17 }catch(IOException e) {
18
19 thrownewRuntimeException(e);
20
21 }
22
23
24
25 returncompare(key1, key2);// compare them
26
27 }

Compare方法的一个缺省实现方式,根据接口key1,ke2反序列化为对象再进行比较。

利用Buffer为桥接中介,把字节数组存储为buffer后,调用key1WritableComparable)的反序列化方法,再来比较key1,ke2,由此处可以看出,该compare方法是将要比较的二进制流反序列化为对象,再调用方法第2个重载方法进行比较。

代码5:方法define方法

该方法用于注册WritebaleComparaor对象到注册表中,注意同时该方法也需要同步,代码如下:

1 publicstaticsynchronizedvoiddefine(Class c,
2 WritableComparator comparator) {
3 comparators.put(c, comparator);
4 }

代码5余下诸如readInt的静态方法

---------------------------------------------------------------------

这些方法用于实现WritableComparable的各种实例,例如IntWritable实例:内部类Comparator类需要根据自己的IntWritable类型重载WritableComparator里面的compare()方法,可以说WritableComparator里面的compare()方法只是提供了一个缺省的实现,而真正的compare()方法实现需要根据自己的类型如IntWritable进行重载,所以WritableComparator方法中的那些readInt..等方法只是底层的封装的一个实现,方便内部Comparator进行调用而已。

下面我们着重看下BooleanWritable类的内置RawCompartor<T>的实现过程:

01 /**
02 * A Comparator optimized for BooleanWritable.
03 */
04 publicstaticclassComparatorextendsWritableComparator {
05 publicComparator() {//调用父类的Constructor初始化keyClass=BooleanWrite.class
06 super(BooleanWritable.class);
07 }
08 //重写父类的序列化比较方法,用些类用到父类提供的缺省方法
09 publicintcompare(byte[] b1,ints1,intl1,
10 byte[] b2,ints2,intl2) {
11 booleana = (readInt(b1, s1) ==1) ?true:false;
12 booleanb = (readInt(b2, s2) ==1) ?true:false;
13 return((a == b) ?0: (a ==false) ? -1:1);
14 }
15 }
16 //注册
17 static{
18 WritableComparator.define(BooleanWritable.class,newComparator());
19 }

总结:

hadoop 类似于Java的类包,即提供了Comparable接口(对应于writableComparable接口)和Comparator类(对应于RawComparator类)用于实现序列化的比较,在hadoop 的IO包中已经封装了JAVA的基本数据类型用于序列化和反序列化,一般自己写的类实现序列化和反序列化需要继承WritableComparable接口并且内置一个Comparator(继承于WritableComparator)的格式来实现自己的对象。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值