我在MR作业的地图阶段使用自定义可写类作为VALUEOUT,其中类有两个字段,org.apache.hadoop.io.Text
和org.apache.hadoop.io.MapWritable
。在我的reduce函数中,我遍历每个键的值,然后执行两个操作,1。过滤,2。聚合。在过滤器中,我有一些规则来检查MapWritable中的某些值(键为Text
,值为IntWritable
或DoubleWritable
)是否满足某些条件,然后我只是将它们添加到一个ArrayList。在过滤操作结束时,我有一个自定义可写对象的筛选列表。在聚合阶段,当我访问对象时,事实证明,成功过滤的最后一个对象已经覆盖了arraylist中的所有其他对象。在对SO上的列表进行了一些类似的问题,其中最后一个对象覆盖了所有其他对象,我确认我没有静态字段,也没有通过设置不同的值重新使用相同的自定义可写(这被引用为可能的原因一个问题)。对于reducer中的每个键,我确保CustomWritable
,Text
键和MapWritable
是新对象。
此外,我还通过消除过滤器和放大器进行了简单的测试。我的reduce中的聚合操作只是迭代了值,并使用for循环将它们添加到ArrayList中。在循环中,每次我将CustomWritable添加到列表中时,都记录了List的所有内容的值。我在将元素添加到列表之前和之后记录。两个日志都表明前一组元素已被覆盖。我对这怎么可能发生很困惑。只要循环for ( CustomWritable result : values )
访问值的可迭代中的下一个元素,就会修改列表内容。我无法弄清楚这种行为的原因。如果有人能够对此有所了解,那将会非常有帮助。感谢。
答案 0 :(得分:1)
reducer中的“values”迭代器在迭代时重用该值。这是一种用于性能和更小内存占用的技术。在幕后,Hadoop将下一条记录反序列化为同一个Java对象。如果你需要“记住”一个对象,你需要克隆它。
您可以利用Writable接口并使用原始字节填充新对象。
IntWritable first = WritableUtils.clone(values.next(), context.getConfiguration());
IntWritable second = WritableUtils.clone(values.next(), context.getConfiguration());