Is het mogelijk om iterators in Java samen te voegen?

Is het mogelijk om iterators in Java samen te voegen? Ik heb twee iterators en ik wil ze combineren / samenvoegen zodat ik hun elementen in één keer (in dezelfde lus) in plaats van twee stappen kan herhalen. Is dat mogelijk?

Merk op dat het aantal elementen in de twee lijsten verschillend kan zijn, daarom is één lus over beide lijsten niet de oplossing.

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

Antwoord 1, autoriteit 100%

Guava(voorheen Google Collections) heeft Iterators.concat.


Antwoord 2, autoriteit 41%

Ook de Apache Commons Collectionhebben verschillende klassen voor het manipuleren van Iterators, zoals de IteratorChain, dat wikkelt een aantal iterators in.


Antwoord 3, autoriteit 33%

U zou uw eigen implementatie van de iterator-interface kunnen maken die de iterators herhaalt:

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 */ }
}

(Ik heb voor de beknoptheid geen generieke geneesmiddelen aan de Iterator toegevoegd.)De implementatie is niet al te moeilijk, maar niet de meest triviale, je moet bijhouden welke iteratordie u momenteel aan het herhalen bent, en als u next()aanroept, moet u zo ver mogelijk door de iterators heen gaan totdat u een hasNext()vindt dat retourneert true, of je bereikt het einde van de laatste iterator.

Ik ben niet op de hoogte van een implementatie die hiervoor al bestaat.

Bijwerken:
Ik heb gestemd op Andrew Duffy’santwoord: je hoeft het wiel niet opnieuw uit te vinden. Ik moet Guava echt nader onderzoeken.

Ik heb nog een constructor toegevoegd voor een variabel aantal argumenten – bijna off-topic, want hoe de klasse hier is geconstrueerd is niet echt van belang, alleen het concept van hoe het werkt.


Antwoord 4, autoriteit 27%

Ik heb al een tijdje geen Java-code geschreven, en dit maakte me nieuwsgierig of ik het nog steeds “heb”.

Eerste poging:

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

Je zounatuurlijk meer Collection-klassen kunnen gebruiken in plaats van een pure array + indexteller, maar dit voelt eigenlijk een beetje schoner aan dan het alternatief. Of ben ik gewoon bevooroordeeld om tegenwoordig voornamelijk C te schrijven?

Hoe dan ook, daar ga je. Het antwoord op je vraag is “ja, waarschijnlijk”.


Antwoord 5, autoriteit 12%

public class IteratorJoin<T> implements Iterator<T> {
    private final Iterator<T> first, next;
    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();
    }
}

Antwoord 6, autoriteit 10%

Vanaf Java 8 en hoger kan dit worden gedaan zonder externe afhankelijkheden met behulp van Stream-API. Dit maakt het ook mogelijk om de iterator samen te voegen met andere soorten streams.

Streams.concat(StreamSupport.stream(<iter1>, false), StreamSupport.stream(<iter2>, false));

Antwoord 7, autoriteit 10%

verplaats je lus naar een methode en geef de iterator door aan methode.

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

Antwoord 8, autoriteit 8%

een iterator komt UIT een verzameling of een set.
waarom niet de reeds beschikbare methode gebruiken
Collection.addAll(Collection c);
en maak vervolgens uw iterator van het laatste object.
op deze manier zal je iterator alle inhoud van beide collecties herhalen.


Antwoord 9, autoriteit 6%

Je kunt mijn versievan een uitbreidbare iterator gebruiken. Het gebruikt een dubbele wachtrij van iterators, wat voor mij logisch is:

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

Antwoord 10, autoriteit 2%

Ik zou het originele ontwerp aanpassen van:

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

Naar zoiets als:

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

Antwoord 11, autoriteit 2%

De samengevoegde iterator:

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

De concatmethode om een ​​Iterablete maken:

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

Eenvoudige JUnit-test:

@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++;
    }
}

Antwoord 12, autoriteit 2%

Je kunt ConcatIteratorvan Cactoos:

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

Controleer ook ConcatIterable, die Iterables samenvoegt.


Antwoord 13, autoriteit 2%

In de Apache Commons-collectiesis public static <E> Iterator<E> org.apache.commons.collections4.IteratorUtils.chainedIterator(Collection<Iterator<? extends E>> iterators)dat zegt

Krijgt een iterator die de ene na de andere verzameling iterators doorloopt.

wat zou moeten zijn wat je wilt.

import java.util.Arrays;
import java.util.Iterator;
import org.apache.commons.collections4.IteratorUtils;
//also works: import org.apache.commons.collections.IteratorUtils;
class Scratch {
    public static void main( String[] args ) {
        final Iterator<String> combinedIterator = IteratorUtils.chainedIterator(
                Arrays.asList( "a", "b", "c" ).iterator(),
                Arrays.asList( "1", "2", "3" ).iterator()
            );
        while( combinedIterator.hasNext() ){
            System.out.println( combinedIterator.next() );
        }
        // "abc123" will have been printed out
    }
}

Antwoord 14

elk iterator-object heeft een eigen geheugenlocatie (adres), dus je kunt ze niet zomaar “samenvoegen”. behalve als je de klasse iteratoruitbreidt en daar je eigen implementatie schrijft.

Als je te maken hebt met hetzelfde aantal objectenin beide iterators, zou een alternatieve oplossing zijn om twee iterators als volgt in één lus te verwerken:

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

Other episodes