我正在尝试执行一个简单的MapReduce程序,其中Map接受输入,将其分为两部分(key => String和value => Integer) reducer汇总了相应键的值 我每次都得到ClassCastException。 我无法理解代码中导致此错误的内容
我的代码:
import java.io.IOException;
import java.util.Iterator;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.FileInputFormat;
import org.apache.hadoop.mapred.FileOutputFormat;
import org.apache.hadoop.mapred.JobClient;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapred.MapReduceBase;
import org.apache.hadoop.mapred.Mapper;
import org.apache.hadoop.mapred.OutputCollector;
import org.apache.hadoop.mapred.Reducer;
import org.apache.hadoop.mapred.Reporter;
import org.apache.hadoop.mapred.TextInputFormat;
import org.apache.hadoop.mapred.TextOutputFormat;
public class Test {
public static class Map extends MapReduceBase implements
Mapper<LongWritable, Text, String, Integer> {
@Override
public void map(LongWritable key, Text value,
OutputCollector<String, Integer> output, Reporter reporter)
throws IOException {
String line = value.toString();
String[] lineParts = line.split(",");
output.collect(lineParts[0], Integer.parseInt(lineParts[1]));
}
}
public static class Reduce extends MapReduceBase implements
Reducer<String, Integer, String, Integer> {
@Override
public void reduce(String key, Iterator<Integer> values,
OutputCollector<String, Integer> output, Reporter reporter)
throws IOException {
int sum = 0;
while (values.hasNext()) {
sum = sum + values.next();
}
output.collect(key, sum);
}
}
public static void main(String[] args) throws Exception {
JobConf conf = new JobConf(Test.class);
conf.setJobName("ProductCount");
conf.setMapOutputKeyClass(String.class);
conf.setMapOutputValueClass(Integer.class);
conf.setOutputKeyClass(String.class);
conf.setOutputValueClass(Integer.class);
conf.setMapperClass(Map.class);
conf.setReducerClass(Reduce.class);
conf.setInputFormat(TextInputFormat.class);
conf.setOutputFormat(TextOutputFormat.class);
FileInputFormat.setInputPaths(conf, new Path(args[0]));
FileOutputFormat.setOutputPath(conf, new Path(args[1]));
JobClient.runJob(conf);
}
}
示例数据:
abc,10
abc,10
abc,10
def,9
def,9
以下是堆栈跟踪。它与我的键值有关吗?
14/02/11 23:57:35 INFO mapred.JobClient: Task Id : attempt_201402110240_0013_m_000001_2, Status : FAILED
java.lang.ClassCastException: class java.lang.String
at java.lang.Class.asSubclass(Class.java:3018)
at org.apache.hadoop.mapred.JobConf.getOutputKeyComparator(JobConf.java:795)
at org.apache.hadoop.mapred.MapTask$MapOutputBuffer.<init>(MapTask.java:816)
at org.apache.hadoop.mapred.MapTask.runOldMapper(MapTask.java:382)
at org.apache.hadoop.mapred.MapTask.run(MapTask.java:324)
at org.apache.hadoop.mapred.Child$4.run(Child.java:268)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.Subject.doAs(Subject.java:396)
at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1115)
at org.apache.hadoop.mapred.Child.main(Child.java:262)
Exception in thread "main" java.io.IOException: Job failed!
at org.apache.hadoop.mapred.JobClient.runJob(JobClient.java:1246)
at Test.main(Test.java:69)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.hadoop.util.RunJar.main(RunJar.java:186)
答案 0 :(得分:1)
在我看来,好像你没有使用正确的输出类。
来自MapReduce之一Tutorials:
键和值类必须由框架序列化,因此需要实现Writable接口。此外,关键类必须实现WritableComparable接口以便于按框架进行排序。
因此,您应将String.class
替换为Text.class
,将Integer.class
替换为IntWritable.class
。
我希望能解决你的问题。
为什么我不能使用基本的String或Integer类?
Integer和String实现了Java的标准Serializable接口,如docs中所示。问题是MapReduce序列化/反序列化不使用此标准接口的值,而是自己的接口,称为Writable。
那么他们为什么不使用基本的Java接口?
简答:因为效率更高。可编写接口在序列化时省略了类型定义,因为您已经在MapReduce代码中定义了输入/输出的类型。因为你的代码已经知道将要发生什么,而不是像这样序列化一个String:
String: "theStringItself"
它可以序列化为:
theStringItself
正如您所看到的,这可以节省大量内存。
答案很长:阅读这篇真棒blog post。