在 reducer 中迭代自定义可写组件的问题

Issue iterating over custom writable component in reducer

我在 MR 作业的映射阶段使用自定义可写 class 作为 VALUEOUT,其中 class 有两个字段,A org.apache.hadoop.io.Textorg.apache.hadoop.io.MapWritable。在我的 reduce 函数中,我遍历每个键的值并执行两个操作,1. 过滤,2. 聚合。在过滤器中,我有一些规则来检查 MapWritable 中的某些值(键为 Text,值为 IntWritableDoubleWritable)是否满足某些条件,然后我只需将它们添加到一个数组列表。在过滤操作结束时,我有一个自定义可写对象的过滤列表。在聚合阶段,当我访问对象时,结果是最后一个成功过滤的对象已经覆盖了 arraylist 中的所有其他对象。在经历了最后一个对象覆盖所有其他对象的 SO 列表的一些类似问题之后,我确认我没有静态字段,也没有通过设置不同的值来重用相同的自定义可写(这被引用为可能的原因)一个问题)。对于 reducer 中的每个键,我都确保 CustomWritableText 键和 MapWritable 是新对象。

此外,我还执行了一个简单的测试,消除了我的 reduce 中的过滤和聚合操作,只是遍历值并使用 for 循环将它们添加到 ArrayList。在循环中,每次我将 CustomWritable 添加到列表中时,我都会记录列表中所有内容的值。我在将元素添加到列表之前和之后进行了记录。两个日志都显示前一组元素已被覆盖。我对这怎么可能发生感到困惑。一旦循环 for ( CustomWritable result : values ) 访问可迭代值中的下一个元素,列表内容就会被修改。我无法弄清楚这种行为的原因。如果有人能对此有所了解,那将非常有帮助。谢谢。

reducer 中的"values" 迭代器在迭代时重用该值。这是一种用于提高性能和减少内存占用的技术。在幕后,Hadoop 将下一条记录反序列化为相同的 Java 对象。如果您需要 "remember" 一个对象,则需要克隆它。

您可以利用 Writable 接口并使用原始字节填充新对象。

IntWritable first = WritableUtils.clone(values.next(), context.getConfiguration());
IntWritable second = WritableUtils.clone(values.next(), context.getConfiguration());