java - 在MapReduce中使用列表作为值返回相同的值

标签 java list hadoop mapreduce reduce

我有一个MapReduce作业,它输出一个IntWritable作为键,而Point(我创建的实现可写的对象)对象作为map函数的值。然后在reduce函数中,我使用一个for-each循环遍历Points的可迭代过程以创建一个列表:

@Override
public void reduce(IntWritable key, Iterable<Point> points, Context context) throws IOException, InterruptedException {

    List<Point> pointList = new ArrayList<>();
    for (Point point : points) {
        pointList.add(point);
    }
    context.write(key, pointList);
}

问题在于此列表的大小正确,但是每个Point都完全相同。我的Point类中的字段不是静态的,因此我在循环中分别打印了每个点,以确保这些点是唯一的(它们是唯一的)。此外,我创建了一个单独的类,该类仅创建几个点并将它们添加到列表中,这似乎可行,这意味着MapReduce做了我不知道的事情。

修复此问题的任何帮助将不胜感激。

更新:
Mapper类的代码:
private final static IntWritable one = new IntWritable(1);
private Text word = new Text();
private IntWritable firstChar = new IntWritable();
private Point point = new Point();

@Override
public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
    String line = value.toString();
    StringTokenizer tokenizer = new StringTokenizer(line, " ");

    while(tokenizer.hasMoreTokens()) {
        String atts = tokenizer.nextToken();
        String cut = atts.substring(1, atts.length() - 1);
        String[] nums = cut.split(",");

        point.set(Double.parseDouble(nums[0]), Double.parseDouble(nums[1]), Double.parseDouble(nums[2]), Double.parseDouble(nums[3]));
        context.write(one, point);
    }
}

点类:
public class Point implements Writable {

public Double att1;
public Double att2;
public Double att3;
public Double att4;

public Point() {

}

public void set(Double att1, Double att2, Double att3, Double att4) {
    this.att1 = att1;
    this.att2 = att2;
    this.att3 = att3;
    this.att4 = att4;
}

@Override
public void write(DataOutput dataOutput) throws IOException {
    dataOutput.writeDouble(att1);
    dataOutput.writeDouble(att2);
    dataOutput.writeDouble(att3);
    dataOutput.writeDouble(att4);
}

@Override
public void readFields(DataInput dataInput) throws IOException {
    this.att1 = dataInput.readDouble();
    this.att2 = dataInput.readDouble();
    this.att3 = dataInput.readDouble();
    this.att4 = dataInput.readDouble();
}

@Override
public String toString() {
    String output = "{" + att1 + ", " + att2 + ", " + att3 + ", " + att4 + "}";
    return output;
}

最佳答案

问题出在您的 reducer 上。您不想将所有点存储在内存中。它们可能很大,而Hadoop为您解决了这个问题(尽管很尴尬)。

当遍历给定的Iterable<Points>时,每个Point实例都将被重用,因此在给定的时间仅保留一个实例。

这意味着,当您调用points.next()时,将发生以下两件事:

  • Point实例被重新使用并与下一个点数据
  • 一起设置
  • 这与Key实例相同。

  • 在您的情况下,您会在列表中找到仅多次插入的Point实例,并使用最后一个Point的数据进行设置。

    您不应该在 reducer 中保存Writables实例,也不应该克隆它们。

    您可以在这里阅读更多有关此问题的信息https://cornercases.wordpress.com/2011/08/18/hadoop-object-reuse-pitfall-all-my-reducer-values-are-the-same/

    关于java - 在MapReduce中使用列表作为值返回相同的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30781423/

    相关文章:

    java - 将列表带入内存的成本

    hadoop - 使用Pig Latin从文件中获取最大日期

    hadoop 1.x 端口列表——另外 4 个未知端口

    java - Play Framework : could not bind from form

    javascript - 使用 Lodash/Javascript 过滤嵌套数组

    java - Vaadin Hello World 未启动

    python - 查找列表中标签关系的频率(成对相关?)

    sql - 配置单元如何增加特定于条件的值?

    java - 自定义对象的 BlazeDS 和 ArrayList

    java - 如何在 Java 中存储类似 Matrix 或 Table 的数据并有效地从中检索?