MapReduce 排序两种实现方式
MapReduce 排序两种实现方式 都是借助重写 compareTo 方法 实现
方式一:借助 MapReduce 按照 Key 排序特性,在WritableComparable实现类中 重写 compareTo 方法
方式二:在 Reduce 阶段 cleanUp方法中将最终结果封装到实现JavaBean对象使用集合的排序方法
方式三:在 Reduce 阶段 cleanUp方法中将最终结果封装到实现Comparable的实现类使用集合的排序方法
方式一
该方式需要两次 MapReduce
第一次 MapReduce 做分类统计
第二次 MapReduce 实现排序
第一次 MapReduce 做分类统计
-
第一次 MapReduce 做分类统计 Mapper 类
package cn.lhz.hadoop.mapreduce.wordcount03; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; import java.io.IOException; import java.util.StringTokenizer; /** * map 阶段 * Mapper<LongWritable, Text, Text, IntWritable> * 前两个 LongWritable,Text 为 map 输入数据的类型,LongWritable 文本文件偏移量,Text 是读取一行的内容 * 后两个 Text,IntWritable 为 map 输出数据的类型 ,map输出 是一个 key value 数据结构, Text 是 key 的数据类型,IntWritable 是 value 的数据类型 * * @author 李昊哲 * @version 1.0.0 */ public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable> { // 定义map输出的value private final static IntWritable one = new IntWritable(1); // 定义map输出的key private final Text word = new Text(); /** * @param key map输入偏移量 * @param value map输入的内容 * @param context Mapper.Context 可以利用该对象做 map 的输出操作 * @throws IOException IOException * @throws InterruptedException InterruptedException */ @Override protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, IntWritable>.Context context) throws IOException, InterruptedException { // 将输入的内容转为 java 字符串 String line = value.toString(); // 默认分隔为 空格 \t \n \r 可以利用构造方法传入自定义分隔符 StringTokenizer itr = new StringTokenizer(line); while (itr.hasMoreTokens()) { // 获取每一行中的每一个单词 String string = itr.nextToken(); // 封装 map 阶段输出 key word.set(string); // 将读取内容安需求输(map阶段输出) context.write(word, one); } } }
-
第一次 MapReduce 做分类统计 Reducer 类
package cn.lhz.hadoop.mapreduce.wordcount03; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Reducer; import java.io.IOException; /** * reduce 阶段 * Reduce<Text, IntWritable,Text,IntWritable> 中四个泛型 两个为一组 * 前两个 LongWritable,Text 为 map 输出 reduce 输入 数据的类型,map 输出 reduce 输入 是一个 key value 数据结构, Text 是 key 的数据类型,IntWritable 是 value 的数据类型 * 后两个 Text,IntWritable 为 reduce 输出数据的类型 ,reduce 输出 是一个 key value 数据结构, Text 是 key 的数据类型,IntWritable 是 value 的数据类型 * * @author 李昊哲 * @version 1.0.0 */ public class WordCountReduce extends Reducer<Text, IntWritable, Text, IntWritable> { // 定义 reduce 输出 value private IntWritable result = new IntWritable(); @Override protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException { // 单词数量累加的和 int sum = 0; // 遍历迭代器 for (IntWritable value : values) { // 单词标记累加得到单词数量 sum += value.get(); } // 封装 reduce 输出 value result.set(sum); // reduce 输出 context.write(key, result); } }
- 第一次 MapReduce 做分类统计 Job 类
package cn.lhz.hadoop.mapreduce.wordcount03; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hdfs.DistributedFileSystem; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import java.net.URI; /** * 本地提交 mapreduce 任务到 集群 * 1、打包项目 * 2、重新编写代码 添加 本地 jar 文件路径 job.setJar(本地jar文件路径) * 3、本地提交 * 服务器提交 mapreduce 任务到 集群 * 1、打包项目 注意:代码中不允许出现 job.setJar(本地jar文件路径) * 2、上传 jar文件到集群 * 3、提交 hadoop jar hadoop.jar cn.lhz.hadoop.mapreduce.wordcount03.WordCount * * @author 李昊哲 * @version 1.0.0 */ public class WordCount { public static void main(String[] args) throws Exception { // 设置环境变量 hadoop 用户名 为 root System.setProperty("HADOOP_USER_NAME", "root"); // 参数配置对象 Configuration conf = new Configuration(); // 跨平台提交 conf.set("mapreduce.app-submission.cross-platform", "true"); // 本地运行 // conf.set("mapreduce.framework.name", "local"); // 设置默认文件系统为 本地文件系统 // conf.set("fs.defaultFS", "file:///"); // 声明Job对象 就是一个应用 Job job = Job.getInstance(conf, "word count"); // 本地提交启用该行 // job.setJar("F:\\code\\bigdata2024\\bigdata\\hadoop\\target\\hadoop.jar"); // 指定当前Job的驱动类 job.setJarByClass(WordCount.class); // 指定当前Job的 Mapper job.setMapperClass(WordCountMapper.class); // 指定当前Job的 Combiner 注意:一定不能影响最终计算结果 否则 不使用 job.setCombinerClass(WordCountReduce.class); // 指定当前Job的 Reducer job.setReducerClass(WordCountReduce.class); // 设置 map 输出 key 的数据类型 job.setMapOutputValueClass(Text.class); // 设置 map 输出 value 的数据类型 job.setMapOutputValueClass(IntWritable.class); // 设置 reduce 输出 key 的数据类型 job.setOutputKeyClass(Text.class); // 设置 reduce 输出 value 的数据类型 job.setOutputValueClass(IntWritable.class); // 定义 map 输入的路径 注意:该路径默认为hdfs路径 FileInputFormat.addInputPath(job, new Path("/WordCount/input/wcdata.txt")); // 定义 reduce 输出数据持久化的路径 注意:该路径默认为hdfs路径 Path path = new Path("/wordcount/sort/李昊哲"); // 根据配置项获取 HDFS 文件系统 FileSystem fs = path.getFileSystem(conf); if (fs.exists(path)) { // 如果 数据输出目录存在 则将数据输出目录删除 fs.delete(path, true); } FileOutputFormat.setOutputPath(job, dst); // 提交 job System.exit(job.waitForCompletion(true) ? 0 : 1); } }
第二次 MapReduce 实现排序
-
编写JavaBean实现 WritableComparable 重写 compareTo 方法
package cn.lhz.hadoop.mapreduce.wordcount04; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.apache.hadoop.io.WritableComparable; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; /** * mapreduce 排序 * * @author 李昊哲 * @version 1.0.0 */ @Setter @Getter @NoArgsConstructor @AllArgsConstructor public class Word implements WritableComparable<Word> { /** * 单词 */ private String name; /** * 单词数量 */ private int count; @Override public String toString() { return this.name + "\t" + this.count; } @Override public int compareTo(Word word) { // 按照单词数量降序 int x = word.getCount() - this.count; if (x != 0) { return x; } else { // 如果单词数量相同 按照单词 hash 值 升序 return this.name.compareTo(word.getName()); } } /** * 序列化 * * @param dataOutput 输出的数据 * @throws IOException IOException */ @Override public void write(DataOutput dataOutput) throws IOException { dataOutput.writeUTF(this.name); dataOutput.writeInt(this.count); } /** * 反序列化 * 读取输入属性顺序与输出属性顺序一直 * * @param dataInput 输入的数据 * @throws IOException IOException */ @Override public void readFields(DataInput dataInput) throws IOException { this.name = dataInput.readUTF(); this.count = dataInput.readInt(); } }
-
第二次 MapReduce Mapper 类 将实现 WritableComparable 接口的类对象,作为 map 阶段输出的 key 实现排序
package cn.lhz.hadoop.mapreduce.wordcount04; 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.io.IOException; /** * 读取 单词计数 结果 * * @author 李昊哲 * @version 1.0.0 */ public class WordCountMapper extends Mapper<LongWritable, Text, Word, NullWritable> { private final Word outKey = new Word(); private final NullWritable outValue = NullWritable.get(); @Override protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Word, NullWritable>.Context context) throws IOException, InterruptedException { String[] split = value.toString().split("\t"); outKey.setName(split[0]); outKey.setCount(Integer.parseInt(split[1])); context.write(outKey, outValue); } }
-
第二次 MapReduce Reducer 类 接收 map 阶段输出的 key 后,按需求格式输出
package cn.lhz.hadoop.mapreduce.wordcount04; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.mapreduce.Reducer; import java.io.IOException; /** * reduce 阶段 * * @author 李昊哲 * @version 1.0.0 */ public class WordCountReduce extends Reducer<Word, NullWritable, Word, NullWritable> { private final NullWritable outValue = NullWritable.get(); @Override protected void reduce(Word key, Iterable<NullWritable> values, Reducer<Word, NullWritable, Word, NullWritable>.Context context) throws IOException, InterruptedException { context.write(key, outValue); } }
-
第二次 MapReduce job 类
package cn.lhz.hadoop.mapreduce.wordcount04; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hdfs.DistributedFileSystem; 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; import java.net.URI; /** * @author 李昊哲 * @version 1.0.0 */ public class WordCountDriver { public static void main(String[] args) throws Exception { // 设置环境变量 hadoop 用户名 为 root System.setProperty("HADOOP_USER_NAME", "root"); // 参数配置对象 Configuration conf = new Configuration(); // 跨平台提交 conf.set("mapreduce.app-submission.cross-platform", "true"); // 本地运行 // conf.set("mapreduce.framework.name", "local"); // 设置默认文件系统为 本地文件系统 // conf.set("fs.defaultFS", "file:///"); // 声明Job对象 就是一个应用 Job job = Job.getInstance(conf, "word count"); // 本地提交启用该行 // job.setJar("F:\\新疆大学\\code\\bigdata2024\\bigdata\\hadoop\\target\\hadoop.jar"); // 指定当前Job的驱动类 job.setJarByClass(WordCountDriver.class); // 指定当前Job的 Mapper job.setMapperClass(WordCountMapper.class); // 指定当前Job的 Combiner 注意:一定不能影响最终计算结果 否则 不使用 // job.setCombinerClass(WordCountReduce.class); // 指定当前Job的 Reducer job.setReducerClass(WordCountReduce.class); // 设置 map 输出 key 的数据类型 job.setMapOutputValueClass(Word.class); // 设置 map 输出 value 的数据类型 job.setMapOutputValueClass(NullWritable.class); // 设置 reduce 输出 key 的数据类型 job.setOutputKeyClass(Word.class); // 设置 reduce 输出 value 的数据类型 job.setOutputValueClass(NullWritable.class); // 定义 map 输入的路径 注意:该路径默认为hdfs路径 FileInputFormat.addInputPath(job, new Path("/WordCount/result/part-r-00000")); // 定义 reduce 输出数据持久化的路径 注意:该路径默认为hdfs路径 Path path = new Path("/wordcount/sort/李昊哲"); // 根据配置项获取 HDFS 文件系统 FileSystem fs = path.getFileSystem(conf); if (fs.exists(path)) { // 如果 数据输出目录存在 则将数据输出目录删除 fs.delete(path, true); } FileOutputFormat.setOutputPath(job, dst); // 提交 job System.exit(job.waitForCompletion(true) ? 0 : 1); } }
方式二
lambada 表达式 重写 compareTo 方法 借助 List 的 sorted 方法排序
-
编写 JavaBean
package cn.lhz.hadoop.mapreduce.wordcount05; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; /** * @author 李昊哲 * @version 1.0.0 */ @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class Word { /** * 单词 */ private String name; /** * 单词数量 */ private int count; @Override public String toString() { return this.name + "\t" + this.count; } }
-
编写 Mapper 类
package cn.lhz.hadoop.mapreduce.wordcount05; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; /** * @author 李昊哲 * @version 1.0.0 */ @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class Word { /** * 单词 */ private String name; /** * 单词数量 */ private int count; @Override public String toString() { return this.name + "\t" + this.count; } }
-
编写 Reducer 类
package cn.lhz.hadoop.mapreduce.wordcount05; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Reducer; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; /** * reduce 阶段 * Reduce<Text, IntWritable,Text,IntWritable> 中四个泛型 两个为一组 * 前两个 LongWritable,Text 为 map 输出 reduce 输入 数据的类型,map 输出 reduce 输入 是一个 key value 数据结构, Text 是 key 的数据类型,IntWritable 是 value 的数据类型 * 后两个 Text,IntWritable 为 reduce 输出数据的类型 ,reduce 输出 是一个 key value 数据结构, Text 是 key 的数据类型,IntWritable 是 value 的数据类型 * * @author 李昊哲 * @version 1.0.0 */ public class WordCountReduce extends Reducer<Text, IntWritable, Text, IntWritable> { private final List<Word> words = new ArrayList<Word>(); private final Text text = new Text(); private final IntWritable intWritable = new IntWritable(); @Override protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException { // 单词数量累加的和 int count = 0; // 遍历迭代器 for (IntWritable value : values) { // 单词标记累加得到单词数量 count += value.get(); } words.add(new Word(key.toString(), count)); } @Override protected void cleanup(Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException { // 排序 List<Word> collect = words.stream().sorted((a, b) -> { // 降序 int x = b.getCount() - a.getCount(); if (x != 0) { return x; } else { return a.getName().compareTo(b.getName()); } }).collect(Collectors.toList()); // 遍历输出 for (Word word : collect) { text.set(word.getName()); intWritable.set(word.getCount()); context.write(text, intWritable); } } }
-
编写 Job 类
package cn.lhz.hadoop.mapreduce.wordcount05; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hdfs.DistributedFileSystem; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import java.net.URI; /** * 借助 List中的 sorted 方法实训排序 * * @author 李昊哲 * @version 1.0.0 */ public class WordCountDriver { public static void main(String[] args) throws Exception { // 设置环境变量 hadoop 用户名 为 root System.setProperty("HADOOP_USER_NAME", "root"); // 参数配置对象 Configuration conf = new Configuration(); // 跨平台提交 conf.set("mapreduce.app-submission.cross-platform", "true"); // 本地运行 // conf.set("mapreduce.framework.name", "local"); // 设置默认文件系统为 本地文件系统 // conf.set("fs.defaultFS", "file:///"); // 声明Job对象 就是一个应用 Job job = Job.getInstance(conf, "word count"); // 本地提交启用该行 // job.setJar("F:\\code\\bigdata2024\\bigdata\\hadoop\\target\\hadoop.jar"); // 指定当前Job的驱动类 job.setJarByClass(WordCountDriver.class); // 指定当前Job的 Mapper job.setMapperClass(WordCountMapper.class); // 指定当前Job的 Combiner 注意:一定不能影响最终计算结果 否则 不使用 // job.setCombinerClass(WordCountReduce.class); // 指定当前Job的 Reducer job.setReducerClass(WordCountReduce.class); // 设置 map 输出 key 的数据类型 job.setMapOutputValueClass(Text.class); // 设置 map 输出 value 的数据类型 job.setMapOutputValueClass(IntWritable.class); // 设置 reduce 输出 key 的数据类型 job.setOutputKeyClass(Text.class); // 设置 reduce 输出 value 的数据类型 job.setOutputValueClass(IntWritable.class); // 定义 map 输入的路径 注意:该路径默认为hdfs路径 FileInputFormat.addInputPath(job, new Path("/WordCount/input/wcdata.txt")); // 定义 reduce 输出数据持久化的路径 注意:该路径默认为hdfs路径 Path path = new Path("/wordcount/sort/李昊哲"); // 根据配置项获取 HDFS 文件系统 FileSystem fs = path.getFileSystem(conf); if (fs.exists(path)) { // 如果 数据输出目录存在 则将数据输出目录删除 fs.delete(path, true); } FileOutputFormat.setOutputPath(job, dst); // 提交 job System.exit(job.waitForCompletion(true) ? 0 : 1); } }
方式三
JavaBean 实现 Comparable 重写 compareTo 方法 借助TreeSet排序
-
编写 JavaBean
package cn.lhz.hadoop.mapreduce.wordcount06; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; /** * @author 李昊哲 * @version 1.0.0 */ @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class Word implements Comparable<Word> { /** * 单词 */ private String name; /** * 单词数量 */ private int count; @Override public String toString() { return this.name + "\t" + this.count; } @Override public int compareTo(Word word) { // 按照单词数量降序 int x = word.getCount() - this.count; if (x != 0) { return x; } else { // 如果单词数量相同 按照单词 hash 值 升序 return this.name.compareTo(word.getName()); } } }
-
编写 Mapper 类
package cn.lhz.hadoop.mapreduce.wordcount06; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; /** * @author 李昊哲 * @version 1.0.0 */ @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class Word implements Comparable<Word> { /** * 单词 */ private String name; /** * 单词数量 */ private int count; @Override public String toString() { return this.name + "\t" + this.count; } @Override public int compareTo(Word word) { // 按照单词数量降序 int x = word.getCount() - this.count; if (x != 0) { return x; } else { // 如果单词数量相同 按照单词 hash 值 升序 return this.name.compareTo(word.getName()); } } }
-
编写 Reducer 类
package cn.lhz.hadoop.mapreduce.wordcount06; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Reducer; import java.io.IOException; import java.util.Set; import java.util.TreeSet; /** * reduce 阶段 * Reduce<Text, IntWritable,Text,IntWritable> 中四个泛型 两个为一组 * 前两个 LongWritable,Text 为 map 输出 reduce 输入 数据的类型,map 输出 reduce 输入 是一个 key value 数据结构, Text 是 key 的数据类型,IntWritable 是 value 的数据类型 * 后两个 Text,IntWritable 为 reduce 输出数据的类型 ,reduce 输出 是一个 key value 数据结构, Text 是 key 的数据类型,IntWritable 是 value 的数据类型 * * @author 李昊哲 * @version 1.0.0 */ public class WordCountReduce extends Reducer<Text, IntWritable, Text, IntWritable> { private final Set<Word> words = new TreeSet<>(); private final Text text = new Text(); private final IntWritable intWritable = new IntWritable(); @Override protected void reduce(Text key, Iterable<IntWritable> values, Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException { // 单词数量累加的和 int count = 0; // 遍历迭代器 for (IntWritable value : values) { // 单词标记累加得到单词数量 count += value.get(); } words.add(new Word(key.toString(), count)); } @Override protected void cleanup(Reducer<Text, IntWritable, Text, IntWritable>.Context context) throws IOException, InterruptedException { for (Word word : words) { text.set(word.toString()); intWritable.set(word.getCount()); context.write(text, intWritable); } } }
-
编写 Job 类
package cn.lhz.hadoop.mapreduce.wordcount06; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.hdfs.DistributedFileSystem; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; import java.net.URI; /** * 借助 TreeSet 实训排序 * * @author 李昊哲 * @version 1.0.0 */ public class WordCountDriver { public static void main(String[] args) throws Exception { // 设置环境变量 hadoop 用户名 为 root System.setProperty("HADOOP_USER_NAME", "root"); // 参数配置对象 Configuration conf = new Configuration(); // 跨平台提交 conf.set("mapreduce.app-submission.cross-platform", "true"); // 本地运行 // conf.set("mapreduce.framework.name", "local"); // 设置默认文件系统为 本地文件系统 // conf.set("fs.defaultFS", "file:///"); // 声明Job对象 就是一个应用 Job job = Job.getInstance(conf, "word count"); // 本地提交启用该行 // job.setJar("F:\\新疆大学\\code\\bigdata2024\\bigdata\\hadoop\\target\\hadoop.jar"); // 指定当前Job的驱动类 job.setJarByClass(WordCountDriver.class); // 指定当前Job的 Mapper job.setMapperClass(WordCountMapper.class); // 指定当前Job的 Combiner 注意:一定不能影响最终计算结果 否则 不使用 // job.setCombinerClass(WordCountReduce.class); // 指定当前Job的 Reducer job.setReducerClass(WordCountReduce.class); // 设置 map 输出 key 的数据类型 job.setMapOutputValueClass(Text.class); // 设置 map 输出 value 的数据类型 job.setMapOutputValueClass(IntWritable.class); // 设置 reduce 输出 key 的数据类型 job.setOutputKeyClass(Text.class); // 设置 reduce 输出 value 的数据类型 job.setOutputValueClass(IntWritable.class); // 定义 map 输入的路径 注意:该路径默认为hdfs路径 FileInputFormat.addInputPath(job, new Path("/WordCount/input/wcdata.txt")); // 定义 reduce 输出数据持久化的路径 注意:该路径默认为hdfs路径 Path path = new Path("/wordcount/sort/李昊哲"); // 根据配置项获取 HDFS 文件系统 FileSystem fs = path.getFileSystem(conf); if (fs.exists(path)) { // 如果 数据输出目录存在 则将数据输出目录删除 fs.delete(path, true); } FileOutputFormat.setOutputPath(job, dst); // 提交 job System.exit(job.waitForCompletion(true) ? 0 : 1); } }