2010-08-31 26 views
37

¿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?¿Es posible fusionar iteradores en Java?

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<User> pUsers = userService.getPrimaryUsersInGroup(group.getId()); 
Iterator<User> sUsers = userService.getSecondaryUsersInGroup(group.getId()); 

while(pUsers.hasNext()) { 
    User user = pUsers.next(); 
    ..... 
} 

while(sUsers.hasNext()) { 
    User user = sUsers.next(); 
    ..... 
} 

Respuesta

16

Se podría crear su propia implementación de la interfaz Iterator la que itera sobre los iteradores:

public class IteratorOfIterators implements Iterator { 
    private final List<Iterator> iterators; 

    public IteratorOfIterators(List<Iterator> 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 */ } 
} 

(. Yo no he añadido los genéricos al iterador por razones de brevedad) La aplicación no es demasiado difícil, pero no es el más trivial, necesita realizar un seguimiento de qué Iterator está iterando actualmente, y llamando al next() deberá iterar todo lo que pueda a través de los iteradores hasta encontrar un hasNext() que devuelva true, o puede tocar el final del último iterador.

No conozco ninguna implementación que ya exista para esto.

Actualización:
he votado arriba-Andrew Duffy's respuesta - no hay necesidad de reinventar la rueda. Realmente necesito mirar a Guava con más profundidad.

He añadido 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.

+2

Añadir un constructor varargs también y Votaría usted por – Christoffer

46

Guava (anteriormente Google Collections) tiene Iterators.concat.

+0

:-) No parece haber ninguna manera de conseguir que el segundo enlace funciona correctamente :( –

+1

segundo vínculo de trabajo para mí. – Youssef

+0

Sí, funcionó bien para mí de la forma en que originalmente lo tenías. – ColinD

3

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

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

Gracias. Pero aún tengo que llamar al método dos veces. –

+0

Esta me parece ser la solución más simple (sin guayaba) para su caso específico. Sí, tienes que llamar a methodX dos veces, pero deberías hacer dos llamadas a métodos de todos modos, una para fusionar los iteradores y otra para hacer lo que hace el métodoX. Su propia solución con la bandera parece más complicada y probablemente más código. – Alb

0

cada objeto contiene Iterator propia posición de memoria (dirección), por lo que no puede simplemente "fusionar" ellos. excepto si extiende la clase iterator y escribe su propia implementación allí.

Si se trata de la mismo número de objetos tanto en iteradores una solución alternativa sería la de procesar dos iteradores en un bucle como este:

while (iterator1.hasNext() && iterator2.hasNext()) { 
     // code 
    } 
+0

OP no necesariamente necesita iterar ambos a la vez. Él está buscando más iterar uno, luego el otro. Su segunda solución fallará una vez que se haya procesado el primer iterador. – smac89

13

no he escrito el código de Java en una Mientras tanto, y esto me dio curiosidad por saber si todavía lo "entendí".

primer intento:

import java.util.Iterator; 
import java.util.Arrays; /* For sample code */ 

public class IteratorIterator<T> implements Iterator<T> { 
    private final Iterator<T> is[]; 
    private int current; 

    public IteratorIterator(Iterator<T>... 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<Integer> a = Arrays.asList(1,2,3,4).iterator(); 
      Iterator<Integer> b = Arrays.asList(10,11,12).iterator(); 
      Iterator<Integer> c = Arrays.asList(99, 98, 97).iterator(); 

      Iterator<Integer> ii = new IteratorIterator<Integer>(a,b,c); 

      while (ii.hasNext()) 
        System.out.println(ii.next()); 
    } 
} 

Usted podría , por supuesto, usar más clases de colección en lugar de una matriz + índice de contador puro, 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í lo tienes. La respuesta a su pregunta es "sí, probablemente".

3

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

+2

Esto tiene algunos inconvenientes, en particular si desea utilizar iteradores perezosos o si las colecciones son muy grandes. – Fabian

+0

Muchas razones para no usar esto. Los iteradores no tienen que venir de una colección o conjunto. Incluso si lo hicieran, no debe copiar todas esas referencias a menos que sepa que las revisará todas. – Navin

+0

No proviene necesariamente de una colección, vuelve a lo básico, un iterador puede iterar y no necesita ser una colección, lo llaman abstracción. – zakmck

16

También el Apache Commons Collection tiene varias clases para manipular iteradores, como el IteratorChain, que envuelve varios iteradores.

+2

Creo que este método requiere menos recursos, ya que no convierte todos los iteradores en una ArrayList. – guerda

1

me habría refactorizar el diseño original de:

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

Para algo como:

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

Puede utilizar my version de un iterador extensible. Se utiliza una cola de doble extremo de iteradores que para mí tiene sentido:

import java.util.Deque; 
import java.util.Iterator; 
import java.util.concurrent.ConcurrentLinkedDeque; 

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

    public Deque<Iterator<T>> its = new ConcurrentLinkedDeque<Iterator<T>>(); 

    public ExtendableIterator() { 

    } 

    public ExtendableIterator(Iterator<T> 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<T> it) { 
     if (it.hasNext()) { 
      its.addLast(it); 
     } 
    } 
} 
1

El iterador combinada:

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<T> implements Iterator<T> { 

    private final List<Iterable<T>> iterables; 
    private Iterator<T> current; 

    @SafeVarargs 
    public ConcatIterator(final Iterable<T>... 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 <T> Iterable<T> concat(final Iterable<T>... iterables) { 
    return() -> new ConcatIterator<>(iterables); 
} 

JUnit simple prueba:

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

Usted puede tratar de ConcatIteratorCactoos:

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

también comprobar ConcatIterable, que concatena Iterable s.

0

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

public IteratorJoin(Iterator<T> first, Iterator<T> 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(); 
} 

}