1.自定义分区:
2.默认分区:
mapreduce中会对key进行默认hash分区,使用的是Hashpartitioner:
调用Hashpartition中的getpartition方法,里面是使用key的hashcode,
观察Text类,里面有两个字段,byte数组和数组的长度,new Text(“zzd”)时会将字符串变为byte数组,(Longwritable里面是一个long字段,将传入的值转为long类型)。
观察它的hashcode方法,调用的是父类BinaryComparable中的hashcode
Text的继承结构:
观察父类BinaryComparable中的方法:
使用的是WritableComparator中的hashBytes方法:
继而调用hashByte方法:
这就是最终计算hashcode方法:
遍历输入的key转成的字节数组,每次做运算,最终计算出hashcode值。
所以只要放到Text中的字符一样,它们转成的byte数组也一样,那么它们的hashcode一定是一样的,也就会被分到同一个区中。所以可以在map函数外Text t=new Text();,然后在map函数内对t赋值,t.set(xxx),这样避免了频繁new Text对象,hashcode是根据放的值来计算的,不像普通对象,比如你在map外new一个Student s=new Student。在map中每次s.set(xxx) 这样它们的hashcode都是相同的,因为是同一个对象,它不像Text那样是根据传入的值来计算hashcode的,所以map的输出都会被分到同一个分区去,所以如果自定义对象时如果对分区有要求一定要自己重写分区方法,如根据ip分区等。而且reduce端,要重写groupingCompator来确立分组。
分区是这样的,同理排序也是根据传入的值来做的,默认升序。
再来看分组:
3、自定义分组:
4、默认分组:
WritableComparator类:
里面的比较字段:(两两相比) 自定义的key都是要实现WritableComparable接口的。
compare方法:
先将要比较的两个key通过反序列化构建出来key1、key2,因为反序列化时要使用反射来构建对象,所以自定义
然后调用类中另一个comapre方法:
点进去,最终调用的是Comparable接口的compareTo方法:
看里面的注释:其实就是使用自定义对象的equals方法,而自定义对象Order的equals使用的是父类Object中的方法,是使用==来比较,即比较地址!所以这些对象都是不同的,也就不会被分到同一个组中,所以要自己重写分组方法,使相同订单的对象被聚合到同一组中,再传到reduce中去处理。
对于如Text类,比较过程为:
会调用WritableComparator的compare方法:
看一下Text中的比较方法:是根据Text中放的值转化成的字节数组来比较的,所以相同的key会被分到同一组,即传入同一个reduce处理!