Hadoop似乎在对给定reduce调用的值进行迭代的过程中修改了我的key对象

Hadoop似乎在对给定reduce调用的值进行迭代的过程中修改了我的key对象,hadoop,reduce,elastic-map-reduce,Hadoop,Reduce,Elastic Map Reduce,Hadoop版本:0.20.2(在Amazon EMR上) 问题:我有一个在映射阶段编写的自定义密钥,我在下面添加了它。在reduce调用期间,我对给定键的值进行了一些简单的聚合。我面临的问题是,在reduce调用中迭代值的过程中,我的键被更改了,我得到了那个新键的值 我的密钥类型: class MyKey implements WritableComparable<MyKey>, Serializable { private MyEnum type; //MyEnum i

Hadoop版本:0.20.2(在Amazon EMR上)

问题:我有一个在映射阶段编写的自定义密钥,我在下面添加了它。在reduce调用期间,我对给定键的值进行了一些简单的聚合。我面临的问题是,在reduce调用中迭代值的过程中,我的键被更改了,我得到了那个新键的值

我的密钥类型:

 class MyKey implements WritableComparable<MyKey>, Serializable {
    private MyEnum type; //MyEnum is a simple enumeration.
    private TreeMap<String, String> subKeys;

    MyKey() {} //for hadoop
    public MyKey(MyEnum t, Map<String, String> sK) { type = t; subKeys = new TreeMap(sk); }

    public void readFields(DataInput in) throws IOException {
      Text typeT = new Text();
      typeT.readFields(in);
      this.type = MyEnum.valueOf(typeT.toString());

      subKeys.clear();
      int i = WritableUtils.readVInt(in);
      while ( 0 != i-- ) {
        Text keyText = new Text();
        keyText.readFields(in);

        Text valueText = new Text();
        valueText.readFields(in);

        subKeys.put(keyText.toString(), valueText.toString());
    }
  }

  public void write(DataOutput out) throws IOException {
    new Text(type.name()).write(out);

    WritableUtils.writeVInt(out, subKeys.size());
    for (Entry<String, String> each: subKeys.entrySet()) {
        new Text(each.getKey()).write(out);
        new Text(each.getValue()).write(out);
    }
  }

  public int compareTo(MyKey o) {
    if (o == null) {
        return 1;
    }

    int typeComparison = this.type.compareTo(o.type); 
    if (typeComparison == 0) {
        if (this.subKeys.equals(o.subKeys)) {
            return 0;
        }
        int x = this.subKeys.hashCode() - o.subKeys.hashCode();
        return (x != 0 ? x : -1);
    }
    return typeComparison;
  }
}
类MyKey实现可写、可比较、可序列化{
私有MyEnum类型;//MyEnum是一个简单的枚举。
私有树映射子键;
hadoop的MyKey(){}//
公共MyKey(MyEnum t,Map sK){type=t;子key=new TreeMap(sK);}
public void readFields(DataInput in)引发IOException{
文本类型t=新文本();
类型读取字段(输入);
this.type=MyEnum.valueOf(typeT.toString());
子键。清除();
int i=writeableutils.readVInt(in);
而(0!=i--){
Text keyText=新文本();
keyText.readFields(in);
文本值Text=新文本();
valueText.readFields(在中);
put(keyText.toString(),valueText.toString());
}
}
public void write(DataOutput out)引发IOException{
新文本(type.name())。写入(out);
WritableUtils.writeVInt(out,subKeys.size());
对于(每个条目:subKeys.entrySet()){
新文本(each.getKey()).write(out);
新文本(each.getValue()).write(out);
}
}
公共整数比较(MyKey o){
如果(o==null){
返回1;
}
int-typeComparison=this.type.comparieto(o.type);
如果(类型比较==0){
if(this.subKeys.equals(o.subKeys)){
返回0;
}
int x=this.subKeys.hashCode()-o.subKeys.hashCode();
返回(x!=0?x:-1);
}
返回类型比较;
}
}
密钥的这种实现有什么问题吗?以下是我在reduce call中面临的按键组合代码:

reduce(MyKey k, Iterable<MyValue> values, Context context) {
   Iterator<MyValue> iterator = values.iterator();
   int sum = 0;
   while(iterator.hasNext()) {
        MyValue value = iterator.next();
        //when i come here in the 2nd iteration, if i print k, it is different from what it was in iteration 1.
        sum += value.getResult();
   }
   //write sum to context
}
reduce(MyKey k、Iterable值、上下文){
迭代器迭代器=值。迭代器();
整数和=0;
while(iterator.hasNext()){
MyValue=iterator.next();
//当我在第二次迭代中来到这里时,如果我打印k,它与迭代1中的不同。
sum+=value.getResult();
}
//将总和写入上下文
}
在这方面的任何帮助都将不胜感激。

这是预期的行为(至少在新API中)

当调用值Iterable的基础迭代器的
next
方法时,将从排序的映射器/组合器输出中读取下一个键/值对,并检查该键是否仍然与前一个键属于同一组


由于hadoop重新使用传递给reduce方法的对象(只调用同一对象的readFields方法),关键参数“k”的底层内容将随着
值的每次迭代而改变。

听起来可能很奇怪,但是我认为如果您删除compareTo方法的hashcode部分和return-1,那么它应该可以正常工作。谢谢您,托马斯。这听起来确实很奇怪,但我太绝望了,我还是试过了。首先,上述代码有效。我羞愧地承认,我粘贴的代码与我运行的代码完全不同。我只是简单地执行
返回这个.subKeys.hashCode()-o.subKeys.hashCode()
,我知道这是错误的,但我没有修复实际的代码,而是尝试比较两个看起来冲突的键的hashCode。我在测试过程中犯了一个错误,并假设其他东西出错,然后修复了这里的代码。那么您是如何解决这个问题的呢?原因是hashCode(),还是?我在2.7版本中遇到过这种情况。如何避免这种行为?我只想计算我的对象(就像标准的wordCounter示例一样,唯一的区别是我使用自定义类型而不是hadoop文本),但最终我只得到一个对象的所有值的总和。reduce()只从run()窗体调用一次,而不是分别为每个键调用。