该对象需要实现WritableComparable接口。
public class MyClass implements WritableComparable<MyClass>{
private int year;
private int temperature;
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getTemperature() {
return temperature;
}
public void setHot(int temperature) {
this.temperature = temperature;
}
@Override
//反序列化过程
public void readFields(DataInput in) throws IOException {
this.year=in.readInt();
this.temperature=in.readInt();
}
@Override
//序列化过程
public void write(DataOutput out) throws IOException {
out.writeInt(year);
out.writeInt(temperature);
}
@Override
public int compareTo(MyClass o) {
int result=Integer.compare(year, o.getYear());
if(result!=0) {
return result;
}
return Integer.compare(temperature, o.getTemperature());
}
@Override
public String toString() {
return year+"-"+temperature;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(temperature);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + year;
return result;
}
}
需要继承WritableComparator父类。
public class MySort extends WritableComparator{
public MySort() {
super(MyClass.class,true);
}
public int compare(WritableComparable a,WritableComparable b) {
MyClass o1=(MyClass) a;
MyClass o2=(MyClass) b;
int result=Integer.compare(o1.getYear(), o2.getYear());
if(result!=0) {
return result;//年份升序排序
}
return -Integer.compare(o1.getTemperature(), o2.getTemperature());//若在同一年份,则温度降序排序
}
}
需要继承Partitioner父类。
public class MyPartioner extends Partitioner<MyClass,Text>{
//按年份进行分区,年份相同的一定在一个区里面
@Override
public int getPartition(MyClass key, Text value, int numReduce) {
return (key.getYear()*Integer.MAX_VALUE)%numReduce;
}
}
因为,这里的key为年份|温度,但是要求相同年份的分为一组。故需要重写分组方法。需要继承WritableComparator父类。
public class MyGroup extends WritableComparator{
public MyGroup() {
super(MyClass.class,true);
}
public int compare(WritableComparable a,WritableComparable b) {
MyClass o1=(MyClass) a;
MyClass o2=(MyClass) b;
return Integer.compare(o1.getYear(), o2.getYear());
}
}
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
Configuration conf=new Configuration();
Job job=Job.getInstance(conf);
job.setJarByClass(RunJob.class);
job.setMapperClass(HotMapper.class);
job.setReducerClass(HotReduce.class);
job.setOutputKeyClass(MyClass.class);
job.setOutputValueClass(Text.class);
job.setNumReduceTasks(4);
job.setPartitionerClass(MyPartioner.class);
job.setGroupingComparatorClass(MyGroup.class);
job.setSortComparatorClass(MySort.class);
FileInputFormat.addInputPath(job,new Path("/usr/city/input/"));
FileOutputFormat.setOutputPath(job, new Path("/usr/city/output/"));
job.waitForCompletion(true);
}
———————————————————————————————————————
分区:如果不自定义分区类,而使用默认分区时,采取的是对键值进行哈希操作,并与reducetask任务数取模,根据得到的值进行分区。由于默认的reducetask任务数设置为1,因此默认情况下只有1个分区。如果自定义一个分区类,则会按照自定义的方式进行分区。
分组:分组和分区类似,也是用来划分数据集的,只不过更加细粒度。如果不自定义分组类而使用默认分组的话,跟默认分区相同,也是通过比较键值来进行分组。reduce()函数是按照组为操作对象进行统计的。
reducer类处理整个分区的数据,其操作对象是区,一个区调用一次reducer类。而reduce()函数的操作对象是组,也就是分区中有 几个分组就调用几次reduce()函数,reduce()函数对分组对应的集合进行处理。分组详解:分组前先对合并后的分区文件中的记录进行排序,排序后再进行分组。分组是通过对排序后的记录从上往下遍历比对进行的。如果上下两个比对结果为0,则分到同一个组,否则各分一个组。后面的分组与前面的分组无关,只与紧挨着的上一条记录有关。举个例子:就算前面有一个“美国”分组,但是中间隔了一个“日本”分组,则后面再出现“美国”时也不会合并到前面的分组中。因此,如果想按照国家分组的话,应该将国家作为组合键的第一个属性,这样在reduce端排序后得到的就是相同的国家上下挨着了。