问题:对文本中的数字进行排序处理
对数字进行排序体验shuffle排序、自定义分区规则和自定义排序。
首先创建一个TXT文本,文本内容如下。
shuffle是存在于map和reduce之间的一个过程,该过程会根据定义的key值进行默认升序排序。如何设置key值在下面Mapper类中有注释解释。(设置key值要注意修改mapper的输出类型和主类中设置的map输出类不一致会出错。)
程序入口类:
package shuffle;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class NumNDemo {
public static void main(String[] args) throws Exception {
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
//配置主类
job.setJarByClass(NumNDemo.class);
//配置mapper类
job.setMapperClass(NumMapper.class);
//配置reduce类
job.setReducerClass(NumReducer.class);
//指定分区规则
job.setPartitionerClass(NumPartitioner.class);
//设置reduce数量
job.setNumReduceTasks(3);
job.setMapOutputKeyClass(NullWritable.class);// map阶段的输出的key
job.setMapOutputValueClass(IntWritable.class);// map阶段的输出的value
job.setOutputKeyClass(NullWritable.class);// reduce阶段的输出的key
job.setOutputValueClass(IntWritable.class);// reduce阶段的输出的value
//文件存在的地址
FileInputFormat.setInputPaths(job, new Path("./src/main/java/shuffle/num.txt"));
//运行结果的输出地址
FileOutputFormat.setOutputPath(job, new Path("./src/main/java/shuffle/output"));
boolean res = job.waitForCompletion(true);
System.exit(res ? 0 : 1);
}
}
Mapper类:
package shuffle;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.util.TreeMap;
public class NumMapper extends Mapper<LongWritable, Text, NullWritable, IntWritable> {
//在map段进行数据排序处理
//使用TreeMap进行排序
private TreeMap<Integer, String> repToRecordMap = new TreeMap<Integer, String>();
//10 3 8 7 6 5 1 2 9 4
//11 12 17 14 15 20
//19 18 13 16
@Override
public void map(LongWritable key, Text value, Context context) {
String line = value.toString();
String[] nums = line.split(" ");
System.out.print("<");
for (String num : nums) {
int a=Integer.parseInt(num);
System.out.print(a+";");
//自定义排序
repToRecordMap.put(a, " ");
//只保留前5个
// if (repToRecordMap.size() > 5) {
// repToRecordMap.remove(repToRecordMap.firstKey());
// }
}System.out.println(">");
}
@Override
protected void cleanup(Context context) {
//对自定义排序的内容进行类型处理,然后交给reduce处理
for (Integer i : repToRecordMap.keySet()) {
// System.out.println("keySet:" + i);
try {
//此处可以设置key值,从而在shuffle中根据该key值进行升序排序
//此处key设置为NullWritable.get(),没有利用shuffle的排序。
context.write(NullWritable.get(), new IntWritable(i));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Reduce类:在这里使用TreeMap可以进行自定义排序规则,同时也可以在这里设置分区后 一个分区内的数据排序情况。(TreeMap怎么使用这里不再赘述)
package shuffle;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
import java.util.Comparator;
import java.util.TreeMap;
class Compareclass implements Comparator<Integer> {
//返回一个基本类型的整型,谁大谁排后面(升序).
//返回负数表示:o1 小于o2
//返回0表示:表示:o1和o2相等
//返回正数表示:o1大于o2。
//默认用o1-o2,创建TreeMap对象时可以不用这个继承类,但是要降序,必须修改compare里面的逻辑o2-o1
//谁大谁排在前面(降序)用o2-o1
@Override
//在reduce端进行排序处理
//修改排列顺序
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
return o1 - o2;
}
}
public class NumReducer extends Reducer<NullWritable, IntWritable, NullWritable, IntWritable> {
Comparator comparetor = new Compareclass();//TreeMap对象可以使用降序的比较器
private TreeMap<Integer, String> repToRecordMap =
new TreeMap<Integer, String>(comparetor); //如果参数为空是默认升序比较器
public void reduce(NullWritable key, Iterable<IntWritable> values, Context context)
throws IOException, InterruptedException {
System.out.println("Reduce输入:");
for (IntWritable value : values) {
int a = value.get();
System.out.print(a + ";");
repToRecordMap.put(a, " ");
//只保留前5个数据
// if (repToRecordMap.size() > 5) {
// repToRecordMap.remove(repToRecordMap.firstKey());
// }
}
System.out.println();
System.out.println("Reducer计算结果:");
System.out.print("<");
for (Integer i : repToRecordMap.keySet()) {
System.out.print(i + ";");
context.write(NullWritable.get(), new IntWritable(i));
}
System.out.println(">");
}
}
建立分区规则:在这里设置分区的规则,我在这里设置小于10的放在第一个分区,小于20大于10的放在第二个分区,其他的放在第三个分区。注意:分区是从0开始的要注意getPartition()方法的返回值,否则会出错。
package shuffle;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapreduce.Partitioner;
/**
* @auther start
* @create 2023-11-12 20:18
*/
public class NumPartitioner extends Partitioner<NullWritable, IntWritable> {
@Override
//自定义分区规则,数据在v2中
public int getPartition(NullWritable k2, IntWritable v2, int numPartitions) {
System.out.println("----------Partition--------------");
int i = v2.get();
if (i < 10) {
return 0;
} else if (i < 20) {
return 1;
} else
return 2;
}
}
结果: