присоединиться к нескольким итераторам в Java - PullRequest
12 голосов
/ 08 февраля 2012

Кто-нибудь знает, как объединить несколько итераторов в Java? Решение, которое я нашел, сначала проходит один итератор, а затем переходит к следующему. Однако я хочу, чтобы при вызове next () сначала возвращался первый элемент первого итератора. В следующий раз, когда вызывается next (), он возвращает первый элемент из второго итератора и т. Д.

Спасибо

Ответы [ 4 ]

10 голосов
/ 08 февраля 2012

Использование Гуавы AbstractIterator для простоты:

final List<Iterator<E>> theIterators;
return new AbstractIterator<E>() {
  private Queue<Iterator<E>> queue = new LinkedList<Iterator<E>>(theIterators);
  @Override protected E computeNext() {
    while(!queue.isEmpty()) {
      Iterator<E> topIter = queue.poll();
      if(topIter.hasNext()) {
        E result = topIter.next();
        queue.offer(topIter);
        return result;
      }
    }
    return endOfData();
  }
};

Это даст вам желаемый «чередующийся» порядок, он достаточно умен для работы с коллекциями разных размеров и достаточно компактен. (Вы можете использовать ArrayDeque вместо LinkedList для скорости, при условии, что вы на Java 6+.)

Если вы действительно, действительно не можете терпеть другую стороннюю библиотеку, вы можете более или менее сделать то же самое с некоторой дополнительной работой, например:

return new Iterator<E>() {
  private Queue<Iterator<E>> queue = new LinkedList<Iterator<E>>(theIterators);
  public boolean hasNext() {
    // If this returns true, the head of the queue will have a next element
    while(!queue.isEmpty()) {
      if(queue.peek().hasNext()) {
        return true;
      }
      queue.poll();
    }
    return false;
  }
  public E next() {
    if(!hasNext()) throw new NoSuchElementException();
    Iterator<E> iter = queue.poll();
    E result = iter.next();
    queue.offer(iter);
    return result;
  }
  public void remove() { throw new UnsupportedOperationException(); }
};

Для справки, поведение "все из iter1, все из iter2 и т. Д." Также можно получить с помощью Iterators.concat(Iterator<Iterator>) и его перегрузок.

3 голосов
/ 08 февраля 2012

Звучит так, как будто вы хотите чередование .Как то так - совершенно не проверено ...

public class InterleavingIterable<E> implements Iterable<E> {

    private final Iterable<? extends E> first;
    private final Iterable<? extends E> second;

    public InterleavingIterable(Iterable<? extends E> first,
                                Iterable<? extends E> second) {
        this.first = first;
        this.second = second;
    }

    public Iterator<E> iterator() {
        return new InterleavingIterator<E>(first.iterator(),
                                           second.iterator());
    }

    private static class InterleavingIterator<E> implements Iterator<E> {

        private Iterator<? extends E> next;
        private Iterator<? extends E> current;

        private InterleavingIterator(Iterator<? extends E> first,
                                     Iterator<? extends E> second) {
            next = first;
            current = second;
        }

        public boolean hasNext() {
            return next.hasNext() || (current != null && current.hasNext());
        }

        public E next() throws NoSuchElementException {
            if (next.hasNext()) {
                E ret = next.next();
                if (current != null) {
                    Iterator<? extends E> tmp = current;
                    current = next;
                    next = tmp;
                }
                return ret;
            } else {
                // Nothing left in next... check "current"
                if (current == null || !current.hasNext()) {
                    throw new NoSuchElementException();
                }
                next = current;
                current = null;
                return current.next();
            }
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}
0 голосов
/ 08 февраля 2012

Редактировать: Ой, неверно истолковал ваш вопрос.На самом деле вам нужен чередующийся итератор вместо составного итератора:

class InterleavingIterator<T> implements Iterator<T> {

    private final Iterator<T> internalIter;

    public InterleavingIterator(final Iterator<T>... iterators) {
        final LinkedList<Iterator<T>> iteratorQueue = new LinkedList<Iterator<T>>();
        for (final Iterator<T> loopIter : iterators) {
            if (loopIter.hasNext()) {
                iteratorQueue.push(loopIter);
            }
        }

        // create the interleaving
        final LinkedList<T> internalList = new LinkedList<T>();
        while (!iteratorQueue.isEmpty()) {
            final Iterator<T> loopIter = iteratorQueue.pop();
            internalList.add(loopIter.next());
            if (loopIter.hasNext()) {
                iteratorQueue.push(loopIter);
            }
        }
        internalIter = internalList.iterator();
    }

    public boolean hasNext() {
        return internalIter.hasNext();
    }

    public T next() {
        return internalIter.next();
    }

    public void remove() {
        throw new UnsupportedOperationException("remove() unsupported");
    }
}

Завершить редактирование.

Вам необходимо использовать составной итератор, например:

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;

public class CompoundIterator<T> implements Iterator<T> {

    private final LinkedList<Iterator<T>> iteratorQueue;
    private Iterator<T> current;

    public CompoundIterator(final Iterator<T>... iterators) {
        this.iteratorQueue = new LinkedList<Iterator<T>>();
        for (final Iterator<T> iterator : iterators) {
            iteratorQueue.push(iterator);
        }
        current = Collections.<T>emptyList().iterator();
    }

    public boolean hasNext() {
        final boolean curHasNext = current.hasNext();
        if (!curHasNext && !iteratorQueue.isEmpty()) {
            current = iteratorQueue.pop();
            return current.hasNext();
        } else {
            return curHasNext;
        }
    }

    public T next() {
        if (current.hasNext()) {
            return current.next();
        }
        if (!iteratorQueue.isEmpty()) {
            current = iteratorQueue.pop();
        }
        return current.next();
    }

    public void remove() {
        throw new UnsupportedOperationException("remove() unsupported");
    }
}
0 голосов
/ 08 февраля 2012

Самый простой подход -

for(Type1 t1: collection1)
    for(Type2 t2: collection2)

Это будет работать, если вы хотите, чтобы он выполнял объединение коллекций.

Если вы хотите перебрать две коллекции, я бы просто использовал две петли или создал коллекцию с обеими.

for(Type t1: collection1)
   process(t1);

for(Type t2: collection2)
   process(t2);

Если вы хотите чередовать Итераторы, вы можете использовать массив.

Iterator[] iters = { iter1, iter2, ... };
boolean finished;
do {
  finished = true;
  for(Iterator it: iters) {
    if (it.hasNext()) {
       Object obj = it.next();
       // process
       finished = false;
    }
  }
} while(!finished);
...