web-dev-qa-db-ja.com

値を2回繰り返す(MapReduce)

引数としてイテレータを受け取り、値を2回繰り返したいと思います。

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

出来ますか ?どうやって ?署名は、私が使用しているフレームワーク(つまり、Hadoop)によって課されます。

-編集-
最後に、reduceメソッドの実際の署名はiterableを使用します。私はこれに惑わされました wikiページ (これは実際に私が見つけた唯一の非推奨ではない(しかし間違った)ワードカウントの例です)。

18
log0

もう一度反復する場合は、イテレータから値をキャッシュする必要があります。少なくとも、最初の反復とキャッシングを組み合わせることができます。

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);
}

(あなたがあなた自身のコメントでこの解決策に言及したことを知って、コードで答えを追加するだけです;))


whyキャッシュなしでは不可能です:Iteratorはインターフェイスを実装するものであり、Iteratorオブジェクトが実際に値を格納するという単一の要件はありません。イテレータをリセットする(不可能)か、クローンを作成する(繰り返し:不可能)必要がある場合は、2回繰り返します。

クローン作成/リセットが意味をなさないイテレータの例を示すには:

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"); 
  }
}
10
Andreas_D

残念ながら、Andreas_Dの回答のように、値をキャッシュしないとこれは不可能です。

ReducerIterableではなくIteratorを受け取る新しいAPIを使用しても、2回繰り返すことはできません。次のようなことを試してみるのは非常に魅力的です。

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

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

しかし、これは実際には機能しません。そのIteratoriterator()メソッドから受け取るIterableは特別です。値がすべてメモリにあるとは限りません。 Hadoopがディスクからそれらをストリーミングしている可能性があります。それらは実際にはCollectionに支えられていないため、複数の反復を許可することは簡単ではありません。

これは、ReducerおよびReduceContextコードで確認できます。

ある種のCollectionに値をキャッシュするのが最も簡単な答えかもしれませんが、大規模なデータセットを操作している場合は、ヒープを簡単に破壊できます。問題の詳細を教えていただければ、複数回の反復を伴わない解決策を見つけるお手伝いができる場合があります。

14
ajduff574

指定されたイテレータを再利用します。

ただし、もちろん、最初に値を反復処理してから構築されたArrayListを反復処理するときに、ArrayListに値を保存できます(または、いくつかの凝ったコレクションメソッドを使用して最初に直接構築し、次に直接反復処理することもできます) ArrayListを2回。好みの問題です)。

とにかく、そもそもイテレータを渡すのは良いことだと思いますか?イテレータは、コレクション全体を線形スキャンするために使用されます。これが、「巻き戻し」メソッドを公開しない理由です。

別の回答ですでに提案されているように、Collection<T>Iterable<T>などの別のものを渡す必要があります。

10
akappa

イテレータは1回の走査のみです。 一部イテレータタイプは複製可能であり、トラバースする前に複製できる場合がありますが、これは一般的なケースではありません。

実現できるのであれば、代わりに関数にIterableを使用させる必要があります。

6

メソッドのシグネチャを変更できない場合は、 Apache Commons IteratorUtils を使用してIteratorをListIteratorに変換することをお勧めします。値を2回反復する次のメソッド例を検討してください。

void iterateTwice(Iterator<String> it) {
    ListIterator<?> lit = IteratorUtils.toListIterator(it);
    System.out.println("Using ListIterator 1st pass");
    while(lit.hasNext())
        System.out.println(lit.next());

    // move the list iterator back to start
    while(lit.hasPrevious())
        lit.previous();

    System.out.println("Using ListIterator 2nd pass");
    while(lit.hasNext())
        System.out.println(lit.next());
}

上記のようなコードを使用して、コードにリスト要素のコピーを保存せずに値のリストを反復処理することができました

2
anubhava

以下のようにReducerで2回反復しようとしている場合

ListIterator<DoubleWritable> lit = IteratorUtils.toListIterator(it);
System.out.println("Using ListIterator 1st pass");
while(lit.hasNext())
    System.out.println(lit.next());

// move the list iterator back to start
while(lit.hasPrevious())
    lit.previous();

System.out.println("Using ListIterator 2nd pass");
while(lit.hasNext())
    System.out.println(lit.next());

としてのみ出力します

Using ListIterator 1st pass
5.3
4.9
5.3
4.6
4.6
Using ListIterator 2nd pass
5.3
5.3
5.3
5.3
5.3

正しい方法で取得するには、次のようにループする必要があります。

ArrayList<DoubleWritable> cache = new ArrayList<DoubleWritable>();
 for (DoubleWritable aNum : values) {
    System.out.println("first iteration: " + aNum);
    DoubleWritable writable = new DoubleWritable();
    writable.set(aNum.get());
    cache.add(writable);
 }
 int size = cache.size();
 for (int i = 0; i < size; ++i) {
     System.out.println("second iteration: " + cache.get(i));
  }

出力

first iteration: 5.3
first iteration: 4.9
first iteration: 5.3
first iteration: 4.6
first iteration: 4.6
second iteration: 5.3
second iteration: 4.9
second iteration: 5.3
second iteration: 4.6
second iteration: 4.6
1

出来るよ

MarkableIterator<Text> mitr = new MarkableIterator<Text>(values.iterator());
mitr.mark();
while (mitr.hasNext()) 
{
//do your work
}
mitr.reset();
while(mitr.hasNext()) 
{
//again do your work
}
  1. 参照リンク2

  2. 参照リンク2

1
Meeran0823

値を変更したい場合は、listIteratorを使用してからset()メソッドを使用する方が良いと思います。

ListIterator lit = list.listIterator();
while(lit.hasNext()){
   String elem = (String) lit.next();
   System.out.println(elem);
   lit.set(elem+" modified");
}
lit = null; 
lit = list.listIterator();
while(lit.hasNext()){
   System.out.println(lit.next());
}

.previous()を呼び出す代わりに、同じリストイテレータオブジェクトで.listIterator()の別のインスタンスを取得します。

0
yev

たくさんの試行錯誤を検索して実行した後、私は解決策を見つけました。

  1. 新しいコレクションを宣言します(たとえば、cache)(リンクリストまたはArraylistまたはその他)

  2. 最初の反復内で、以下の例のように現在のイテレータを割り当てます。

    cache.add(new Text(current.get()))  
    
  3. キャッシュを反復処理します。

    for (Text count : counts) {
        //counts is iterable object of Type Text
        cache.add(new Text(count.getBytes()));
    }
    for(Text value:cache) {
        // your logic..
    }
    
0
Keval Shah

これを試して:

    ListIterator it = list.listIterator();

    while(it.hasNext()){

        while(it.hasNext()){
            System.out.println("back " + it.next() +" "); 
        }
        while(it.hasPrevious()){
            it.previous();
        }
    }
0
Prashob