¿Es posible fusionar iteradores en Java?

¿Es posible fusionar iteradores en Java? Tengo dos iteradores y quiero combinarlos para poder repetir sus elementos de una vez (en el mismo ciclo) en lugar de dos pasos. ¿Es eso posible?

Tenga en cuenta que el número de elementos en las dos listas puede ser diferente, por lo tanto, un bucle en ambas listas no es la solución.

Iterator pUsers = userService.getPrimaryUsersInGroup(group.getId()); Iterator sUsers = userService.getSecondaryUsersInGroup(group.getId()); while(pUsers.hasNext()) { User user = pUsers.next(); ..... } while(sUsers.hasNext()) { User user = sUsers.next(); ..... } 

Guava (anteriormente Google Collections) tiene Iterators.concat .

Puede crear su propia implementación de la interfaz Iterator que itera sobre los iteradores:

 public class IteratorOfIterators implements Iterator { private final List iterators; public IteratorOfIterators(List iterators) { this.iterators = iterators; } public IteratorOfIterators(Iterator... iterators) { this.iterators = Arrays.asList(iterators); } public boolean hasNext() { /* implementation */ } public Object next() { /* implementation */ } public void remove() { /* implementation */ } } 

(No he agregado los generics al iterador por razones de brevedad). La implementación no es muy difícil, pero no es la más trivial, necesita realizar un seguimiento de qué Iterator está iterando actualmente, y llamar al next() ‘ Necesitaré iterar todo lo que pueda a través de los iteradores hasta que encuentre un hasNext() que devuelva true , o puede llegar al final del último iterador.

No estoy al tanto de ninguna implementación que ya exista para esto.

Actualizar:
He votado por la respuesta de Andrew Duffy, no hay necesidad de reinventar la rueda. Realmente necesito mirar a Guava con más profundidad.

He agregado otro constructor para una cantidad variable de argumentos, casi saliendo del tema, ya que la forma en que se construye la clase aquí no es realmente interesante, solo el concepto de cómo funciona.

Además, la Colección Apache Commons tiene varias clases para manipular Iteradores, como IteratorChain , que envuelve varios iteradores.

Hace mucho que no escribo el código Java, y esto me dio curiosidad por saber si todavía lo tengo.

Primer bash:

 import java.util.Iterator; import java.util.Arrays; /* For sample code */ public class IteratorIterator implements Iterator { private final Iterator is[]; private int current; public IteratorIterator(Iterator... iterators) { is = iterators; current = 0; } public boolean hasNext() { while ( current < is.length && !is[current].hasNext() ) current++; return current < is.length; } public T next() { while ( current < is.length && !is[current].hasNext() ) current++; return is[current].next(); } public void remove() { /* not implemented */ } /* Sample use */ public static void main(String... args) { Iterator a = Arrays.asList(1,2,3,4).iterator(); Iterator b = Arrays.asList(10,11,12).iterator(); Iterator c = Arrays.asList(99, 98, 97).iterator(); Iterator ii = new IteratorIterator(a,b,c); while ( ii.hasNext() ) System.out.println(ii.next()); } } 

Por supuesto, podría utilizar más clases de Colección en lugar de un puro contador de matriz + índice, pero esto en realidad se siente un poco más limpio que la alternativa. ¿O simplemente estoy predispuesto a escribir principalmente C en estos días?

De todos modos, ahí tienes. La respuesta a su pregunta es “sí, probablemente”.

mueve tu ciclo a un método y pasa el iterador al método.

 void methodX(Iteartor x) { while (x.hasNext()) { .... } } 

un iterador proviene de una colección o un conjunto.
¿por qué no usar el método ya disponible?
Collection.addAll(Collection c);
y luego crea tu iterador a partir del último objeto.
De esta manera, su iterador repetirá todos los contenidos de ambas colecciones.

Refactorizaría el diseño original desde:

 Iterator pUsers = userService.getPrimaryUsersInGroup(group.getId()); Iterator sUsers = userService.getSecondaryUsersInGroup(group.getId()); 

Para algo como:

 Iterator users = userService.getUsersInGroup(group.getId(), User.PRIMARY, User.SECONDARY, ...); 

Puedes usar mi versión de un iterador extensible. Utiliza una cola de iteradores de doble extremo que para mí tiene sentido:

 import java.util.Deque; import java.util.Iterator; import java.util.concurrent.ConcurrentLinkedDeque; public class ExtendableIterator implements Iterator { public Deque> its = new ConcurrentLinkedDeque>(); public ExtendableIterator() { } public ExtendableIterator(Iterator it) { this(); this.extend(it); } @Override public boolean hasNext() { // this is true since we never hold empty iterators return !its.isEmpty() && its.peekLast().hasNext(); } @Override public T next() { T next = its.peekFirst().next(); if (!its.peekFirst().hasNext()) { its.removeFirst(); } return next; } public void extend(Iterator it) { if (it.hasNext()) { its.addLast(it); } } } 

El iterador fusionado:

 import static java.util.Arrays.asList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; public class ConcatIterator implements Iterator { private final List> iterables; private Iterator current; @SafeVarargs public ConcatIterator(final Iterable... iterables) { this.iterables = new LinkedList<>(asList(iterables)); } @Override public boolean hasNext() { checkNext(); return current != null && current.hasNext(); } @Override public T next() { checkNext(); if (current == null || !current.hasNext()) throw new NoSuchElementException(); return current.next(); } @Override public void remove() { if (current == null) throw new IllegalStateException(); current.remove(); } private void checkNext() { while ((current == null || !current.hasNext()) && !iterables.isEmpty()) { current = iterables.remove(0).iterator(); } } } 

El método concat para crear un Iterable :

 @SafeVarargs public static  Iterable concat(final Iterable... iterables) { return () -> new ConcatIterator<>(iterables); } 

Prueba JUnit simple:

 @Test public void testConcat() throws Exception { final Iterable it1 = asList(1, 2, 3); final Iterable it2 = asList(4, 5); int j = 1; for (final int i : concat(it1, it2)) { assertEquals(j, i); j++; } } 

Puedes probar ConcatIterator desde Cactoos :

 Iterator names = new ConcatIterator<>( Arrays.asList("Sarah", "Mary").iterator(), Arrays.asList("Jeff", "Johnny").iterator(), ); 

También verifique ConcatIterable , que concatena s Iterable .

public class IteratorJoin implementa Iterator {Iterator final privado primero, siguiente;

 public IteratorJoin(Iterator first, Iterator next) { this.first = first; this.next = next; } @Override public boolean hasNext() { return first.hasNext() || next.hasNext(); } @Override public T next() { if (first.hasNext()) return first.next(); return next.next(); } 

}

cada objeto Iterator tiene su propia ubicación de memoria ( dirección ), por lo que no puede simplemente “fusionarlos”. excepto si extiendes clase de iterator y escribes tu propia implementación allí.

Si está tratando con el mismo número de objetos en ambos iteradores, una solución alternativa sería procesar dos iteradores en un ciclo como este:

  while (iterator1.hasNext() && iterator2.hasNext()) { // code } 
    Intereting Posts