WordCount的非MapReduce实现和MapReduce在Windows下实现
非MapReduce实现
package SimpleTest;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class WordCountWithMapList {
//path为需要统计的文件存储路径
public static Map<String,Integer> wordcount(String path) throws IOException {
//声明一个Map<String,Integer>用于统计单词出现的次数,Key是单词,Value是单词出现次数
Map<String,Integer> map = new HashMap();
String line = null;
BufferedReader br = new BufferedReader(new FileReader(path));
//逐行读取文件内容,当行不为空时,对行内容按空格、制表符等进行分割
while((line = br.readLine()) !=null){
String[] words = line.split("\\s+");
for(String word:words){
//判断Map中是否已经有该条单词的记录,如果有则将Value加一,如果没有则将Key添加到Map中并将Value置为1
if(map.containsKey(word))
map.put(word,map.get(word)+1);
else
map.put(word,1);
}
}
print(map);
return map;
}
//写一个函数打印出Map内容
public static void print(Map<String,Integer> map){
for(Map.Entry<String,Integer> entry:map.entrySet()){
System.out.println(entry.getKey()+"===="+entry.getValue());
}
}
public static void main(String[] args) throws IOException {
String path = "D:\\testio\\4\\news.txt";
wordcount(path);
}
}
MapReduce在Windows下实现
用到Hadoop的Jar包的项目首先需要引入四个依赖
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.7.7</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-yarn</artifactId>
<version>2.7.7</version>
</dependency>
WordCount程序实现仅用到了hadoop-common和hadoop-client这两个依赖,但是为了日后方便扩展,建议同时添加以上四个依赖。
最基本的MapReduce由三个部分构成
- Map:继承自hadoop-client中的Mapper类,按行读取文件数据,并以Key-value的方式发送到Reduce端
Mapper<参数一,参数二,参数三,参数四>
参数一:必须为LongWritable的类型,是行偏移量,记录当前文件读到哪一行来了
参数二:读取数据的类型,这里主要一定要用Hadoop提供的xxxWritable的类型,因为MapReduce之间的传输需要进行序列化和反序列化的操作,原生的基本数据类型和String类型无法胜任这样的工作
参数三:Map完成之后输出的Value的类型
参数四:Map完成之后输出的Key的类型
package SimpleTest.HadoopMRWindows;
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;
public class MyMap extends Mapper<LongWritable, Text, Text, IntWritable> {
Text mk = new Text();
IntWritable mv = new IntWritable();
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
String line = value.toString();
String[] words = line.split("\\s+");
for(String word:words){
mk.set(word);
mv.set(1);
context.write(mk,mv);
}
}
}
Map中需要做的事情
重写map方法,修改map方法的参数类型
map方法有三个参数
第一个参数是行偏移量,一般不需要修改
第二个参数就是读取的每一行的内容,一般需要对其进行分割,得到单个单词组成的String类型的数组
第三个参数是Context,主要用于Map和Reduce作业的提交
因为map是有多少行就调用多少次,所以为了提高程序的效率节约内存空间,一些对象的声明我们可以放在方外运行
- Reduce
Reducer<参数一,参数二,参数三,参数四>
参数一:从Map端接收过来的Key的类型,应与Map的输出Key类型一致
参数二:从Map端接收过来的value的类型,应与Map的输出value类型一致
参数三:Reduce的输出Key的类型
参数四:Reduce的输出Value的类型
package SimpleTest.HadoopMRWindows;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
public class MyReduce extends Reducer<Text,IntWritable,Text,IntWritable> {
IntWritable mv = new IntWritable();
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
int sum = 0;
for (IntWritable v:values){
sum+=v.get();
}
mv.set(sum);
context.write(key,mv);
}
}
Reduce中需要做的事情
重写reduce方法
reduce方法也有三个参数
第一个参数:输入的Key
第二个参数:输入Key对应Value的迭代器
例如:一个input.txt文件中有两行内容
hello hello bye sup hello
dude guy homie hello
那么执行Key为hello的Reduce时,对应hello的值的迭代器的{(1),(1),(1),(1)}
第三个参数:用于提交作业,提交时同样以Key-Value键值对的形式提交
在Reduce中我们做的主要事情还是把迭代器中的值进行一个整合
- Driver
package SimpleTest.HadoopMRWindows;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
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.io.IOException;
public class MyDriver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
String input = "D:\\testio\\4\\";
String output = "D:\\testio\\4out\\";
Path ip = new Path(input);
Path op = new Path(output);
//声明conf对象,用conf对象去构造一个job,用job设置各种运行类
Configuration conf = new Configuration();
Job job = Job.getInstance(conf);
//指定Driver所在类
job.setJarByClass(MyDriver.class);
//指定Map所在类
job.setMapperClass(MyMap.class);
//指定Reduce所在类
job.setReducerClass(MyReduce.class);
//指定Map输出的Key和Value的类型
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//指定Reduce输出的Key和Value类型
job.setOutputValueClass(Text.class);
job.setOutputKeyClass(IntWritable.class);
//指定读取文件路径
FileInputFormat.addInputPath(job,ip);
//指定文件输出路径,这个路径下必须为空,否则会报错
FileOutputFormat.setOutputPath(job,op);
//如果该路径下存在文件,则删除文件
FileSystem fs = FileSystem.get(conf);
if(fs.exists(op)){
fs.delete(op,true);
}
//运行,并等待运行完成
job.waitForCompletion(true);
}
}