迭代两次值 (MapReduce)

2022-09-02 11:46:25

我收到一个迭代器作为参数,我想迭代值两次。

public void reduce(Pair<String,String> key, Iterator<IntWritable> values,
                   Context context)

可能吗?如何?签名是由我正在使用的框架(即Hadoop)强加的。

-- edit --
最后,该方法的真正签名是带有 .我被这个wiki页面误导了(这实际上是我发现的唯一未弃用(但错误)的字数示例)。reduceiterable


答案 1

不幸的是,如果不像Andreas_D的答案那样缓存值,这是不可能的。

即使使用新的 API,其中 接收 a 而不是 a ,您也无法迭代两次。尝试这样的事情是非常诱人的:ReducerIterableIterator

for (IntWritable value : values) {
    // first loop
}

for (IntWritable value : values) {
    // second loop
}

但这实际上行不通。您从 该方法接收的 方法是特殊的。这些值可能并不都在内存中;Hadoop可能正在从磁盘流式传输它们。它们实际上没有 a 的支持,因此允许多次迭代并非易事。IteratorIterableiterator()Collection

您可以在 和 代码中亲眼看到这一点。ReducerReduceContext

以某种方式缓存值可能是最简单的答案,但是如果您正在操作大型数据集,则可以轻松破坏堆。如果您可以向我们提供有关您的问题的更多详细信息,我们也许能够帮助您找到不涉及多次迭代的解决方案。Collection


答案 2

如果要再次迭代,我们必须缓存迭代器中的值。至少我们可以将第一次迭代和缓存结合起来:

Iterator<IntWritable> it = getIterator();
List<IntWritable> cache = new ArrayList<IntWritable>();

// first loop and caching
while (it.hasNext()) {
   IntWritable value = it.next();
   doSomethingWithValue();
   cache.add(value);
}

// second loop
for(IntWritable value:cache) {
   doSomethingElseThatCantBeDoneInFirstLoop(value);
}

(只是为了添加一个带有代码的答案,知道你在自己的评论中提到了这个解决方案;))


为什么没有缓存就不可能:a是实现接口的东西,没有一个要求,对象实际上存储值。迭代两次,要么必须重置迭代器(不可能)要么克隆它(再次:不可能)。IteratorIterator

举个例子,一个迭代器的例子,其中克隆/重置没有任何意义:

public class Randoms implements Iterator<Double> {

  private int counter = 10;

  @Override 
  public boolean hasNext() { 
     return counter > 0; 
  }

  @Override 
  public boolean next() { 
     count--;
     return Math.random();        
  }      

  @Override 
  public boolean remove() { 
     throw new UnsupportedOperationException("delete not supported"); 
  }
}