Hadoop源码分析:WritableComparator及排序实现方式

  • Post author:
  • Post category:其他



目录


一、WritableComparator部分源码分析


二、Hadoop的Text类分析


三、自定义比较器


Hadoop版本: 3.1.3

通过分析MapReduce比较器获取的源码可知,对Key排序的比较器都是WritableComparator。下面就来一探WritableComparator的源码,看它是如何实现的。(comparator的获取可以参考:

Hadoop源码分析:Comparator的获取

。)

一、WritableComparator部分源码分析

WritableComparator是Comparator的一个实现类,该类定义了一个HashMap集合comparators,通过define()方法把Class c 类的比较器以<c.class,comparator>键值对的方式存入到comparators。换句话说,就是把C类的comparator存放在comparators中,这样在有需要的时候可以根据对应的Class c,获得与之对应的比较器。

public class WritableComparator implements RawComparator, Configurable {
    
    private static final ConcurrentHashMap<Class, WritableComparator> comparators =     
                                        new ConcurrentHashMap();
    public static void define(Class c, WritableComparator comparator) {
        comparators.put(c, comparator);
    }

同时,该类提供了获得比较器的get方法,如下源码。该方法内部调用了Hashmap的get()方法,通过key的值,获得与传入参数key对应的比较器。但comparators中只存储了hadoop所属类的比较器,自定义类的比较器无法获得。

public static WritableComparator get(Class<? extends WritableComparable> c, Configuration conf) {
        WritableComparator comparator = (WritableComparator)comparators.get(c);
        if (comparator == null) {
            forceInit(c);
            comparator = (WritableComparator)comparators.get(c);
            if (comparator == null) {
                comparator = new WritableComparator(c, conf, true);
            }
        }

        ReflectionUtils.setConf(comparator, conf);
        return comparator;
    }

若是自定义类,get()方法无法获得comparator,只能创建新的比较器,那么新的比较器是如何实现可比较的呢?

在WritableComparator类中,封装了三个compare方法,新创建的comparator默认就是使用的该compare方法。从源码可知,3个compare()方法重载,第1个compare()和第3个compare()都是间接调用的第2个compare()方法。

通过第2个compare()可知,比较的Key的类型必须是WritableComparable类,且调用了key类的compareTo()方法。因此,就要求自定义的类必须实现WritableComparable接口,并实现compareTo()。

 public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
        try {
            this.buffer.reset(b1, s1, l1);
            this.key1.readFields(this.buffer);
            this.buffer.reset(b2, s2, l2);
            this.key2.readFields(this.buffer);
            this.buffer.reset((byte[])null, 0, 0);
        } catch (IOException var8) {
            throw new RuntimeException(var8);
        }

        return this.compare(this.key1, this.key2);
    }

    public int compare(WritableComparable a, WritableComparable b) {
        return a.compareTo(b);
    }

    public int compare(Object a, Object b) {
        return this.compare((WritableComparable)a, (WritableComparable)b);
    }

综上分析可知,若是Mapreduce中的Key为自定义类时,从系统comparators集合中无法获取对应的比较器,若是在未提供比较器的情况下,程序内部会创建一个新的comparator,且默认最终调用的方法是自定义类的compareTo()方法。

二、Hadoop的Text类分析

Text类是hadoop的所需类,如上所说,Text类在进行类加载时,就会将对应的比较器存入comparators集合中。下边就从源码的角度分析具体的实现方式。

在Text类中,有个继承了WritableComparator的嵌套类Comparator,如下源码。此Comparator类中声明了一个Compare()方法,用来实现Text的可比较。另外,在Text类中,有一个初始化类的静态代码块,其中执行了define()方法。此方法的作用就是将Text类的comparator存入comparators集合中。Hadoop的其他类IntWritable、DoubleWritable等,实现方式与Text相同。

public class Text extends BinaryComparable implements 
                       WritableComparable<BinaryComparable> {

  static {
        WritableComparator.define(Text.class, new Text.Comparator());
        bytesFromUTF8 = new int[]{...};
        offsetsFromUTF8 = new int[]{0, 12416, 925824, 63447168, -100130688, -2113396608};
    }


    public static class Comparator extends WritableComparator {
        public Comparator() {
            super(Text.class);
        }
        //compare方法
        public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
            int n1 = WritableUtils.decodeVIntSize(b1[s1]);
            int n2 = WritableUtils.decodeVIntSize(b2[s2]);
            return compareBytes(b1, s1 + n1, l1 - n1, b2, s2 + n2, l2 - n2);
        }
    }

三、自定义比较器

通过对Text类比较器的源码分析,可知,自定义比较器需继承WritableComparator接口。从WritableComparable中的compare()方法可知,1、3compare()方法的最终调用都是第2个compare方法。

所以自定义的比较器,需要继承WritableComparator,并重写第2个compare()方法。如下:

public class KeyComparator extends WritableComparator {
    @Override
    public int compare(WritableComparable a, WritableComparable b) {
        Key aKey = (Key)a;
        Key bKey = (Key)b;
        
        return ... ;//此处可以自定义排序规则
    }
}

综合以上分析可知,comparator的优先级是最高的,只要有定义,则会采用comparator定义的比较规则。若没有自定义比较器,或者比较器没有重写compare()方法,则最终还是会采用Key类的compareTo()方法。



版权声明:本文为m0_46589706原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。