Итерация дважды по значениям (MapReduce)

Я получаю iterator в качестве аргумента, и я хотел бы повторять значения в два раза.

public void reduce(Pair key, Iterator values, Context context) 

Является ли это возможным ? Как ? Подпись накладывается той картой, которую я использую (а именно Hadoop).

— редактировать —
Наконец, действительная сигнатура метода reduce имеет iterable . Я был введен в заблуждение этой страницей wiki (которая на самом деле является единственным неисчерпаемым (но неправильным) примером найденного слова).

Мы должны кэшировать значения из iteratorа, если вы хотите повторить итерацию. По крайней мере, мы можем объединить первую итерацию и кеширование:

 Iterator it = getIterator(); List cache = new ArrayList(); // first loop and caching while (it.hasNext()) { IntWritable value = it.next(); doSomethingWithValue(); cache.add(value); } // second loop for(IntWritable value:cache) { doSomethingElseThatCantBeDoneInFirstLoop(value); } 

(просто чтобы добавить ответ с кодом, зная, что вы упомянули это решение в своем собственном комментарии;))


почему это невозможно без кеширования: Iterator – это то, что реализует интерфейс, и нет ни одного требования, что объект Iterator фактически сохраняет значения. Повторите дважды, либо вам придется сбросить iterator (возможно), либо клонировать его (опять же: невозможно).

Чтобы привести пример для iteratorа, где клонирование / сброс не имеет никакого смысла:

 public class Randoms implements Iterator { 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"); } } 

К сожалению, это невозможно без кэширования значений, как в ответе Andreas_D.

Даже используя новый API, где Reducer получает Iterable а не Iterator , вы не можете повторять итерацию дважды. Очень заманчиво попробовать что-то вроде:

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

Но это на самом деле не работает. Iterator вы получаете из этого метода iterator() является специальным. Значения могут быть не все в памяти; Hadoop может передавать их с диска. Они не подкреплены Collection , поэтому нетривиально разрешить несколько итераций.

Вы можете убедиться в этом в коде Reducer и ReduceContext .

Кэширование значений в какой-то Collection может быть самым простым ответом, но вы можете легко взорвать кучу, если работаете на больших наборах данных. Если вы можете дать нам больше подробностей о вашей проблеме, мы сможем помочь вам найти решение, не требующее нескольких итераций.

Повторное использование данного iteratorа, нет.

Но вы можете сохранить значения в ArrayList при первом итерации через них, а затем, конечно, итерации по построенному массиву ArrayList (или вы можете создать его непосредственно в первую очередь, используя некоторые причудливые методы Collection, а затем итерации непосредственно на ArrayList дважды. Это вопрос вкусов).

Во всяком случае, вы уверены, что прохождение Итератора – это, во-первых, хорошая вещь? Итераторы используются для линейного сканирования через коллекцию, поэтому они не выставляют метод «перемотки».

Вы должны передать что-то другое, например Collection или Iterable , как уже было предложено в другом ответе.

Итераторы имеют только один проход. Некоторые типы iteratorов являются клонируемыми, и вы можете клонировать их перед обходом, но это не общий случай.

Вы должны сделать свою функцию вместо Iterable , если вы можете этого достичь.

Если подпись метода не может быть изменена, я бы предложил использовать Apache Commons IteratorUtils для преобразования Iterator в ListIterator. Рассмотрим этот примерный метод для итерации дважды по значениям:

 void iterateTwice(Iterator 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()); } в void iterateTwice(Iterator 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()); } в void iterateTwice(Iterator 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()); } в void iterateTwice(Iterator 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()); } 

Используя вышеприведенный код, я смог перебирать список значений без сохранения копии элементов List в моем коде.

Если мы пытаемся повторить дважды в Reducer, как показано ниже

 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()); в 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()); в 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()); в 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()); 

Мы будем выводить только

 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 cache = new ArrayList(); 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 

Вы можете сделать это

 MarkableIterator mitr = new MarkableIterator(values.iterator()); mitr.mark(); while (mitr.hasNext()) { //do your work } mitr.reset(); while(mitr.hasNext()) { //again do your work } в MarkableIterator mitr = new MarkableIterator(values.iterator()); mitr.mark(); while (mitr.hasNext()) { //do your work } mitr.reset(); while(mitr.hasNext()) { //again do your work } 
  1. Ссылка 2

  2. Ссылка 2

Попробуй это:

  ListIterator it = list.listIterator(); while(it.hasNext()){ while(it.hasNext()){ System.out.println("back " + it.next() +" "); } while(it.hasPrevious()){ it.previous(); } } в  ListIterator it = list.listIterator(); while(it.hasNext()){ while(it.hasNext()){ System.out.println("back " + it.next() +" "); } while(it.hasPrevious()){ it.previous(); } } в  ListIterator it = list.listIterator(); while(it.hasNext()){ while(it.hasNext()){ System.out.println("back " + it.next() +" "); } while(it.hasPrevious()){ it.previous(); } } в  ListIterator it = list.listIterator(); while(it.hasNext()){ while(it.hasNext()){ System.out.println("back " + it.next() +" "); } while(it.hasPrevious()){ it.previous(); } } 

если вы хотите изменить значения, как вы идете, я думаю, что лучше использовать 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()); } в 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()); } в 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 () в том же самом iteratorе списка.

После поиска и выполнения многих попыток и ошибок я нашел решение.

  1. Объявить новую коллекцию (например, cache ) (связанный список или Arraylist или любой другой)

  2. Внутри первой итерации назначьте текущий iterator, как показано ниже:

     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.. } 
  • Почему мы используем if, else, если вместо множественного блока if, если тело является оператором return
  • Байт байта Java содержит отрицательные числа
  • Установить и получить методы в java?
  • Ограничение ли Java на длину имени classа?
  • Как повторно включить плагин Java в Google Chrome 42 на Windows
  • Как проверить правильность данной строки JSON в Java
  • Как преобразовать число в слова в java
  • Как обновить Eclipse для разработчиков Java EE?
  • Как запустить JDK Netbeans?
  • Где я могу найти окончательный Selenium WebDriver для Матрицы совместимости Firefox?
  • Как удалить разрывы строк из файла в Java?
  • Давайте будем гением компьютера.