Search code examples
javacollectionsiterator

How to combine several iterators to one?


Can I return several iterators like one? For example in iterator() method maybe I can combine iterators like one?

import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class Addresses implements Iterable<Address> {
    private final Address main;
    private final List<Address> additional;
    private final List<Address> verified;

    public Addresses(Address main, List<Address> additional, List<Address> verified) {
        this.main = main;
        this.additional = additional;
        this.verified = verified;
    }

    public Address getMain() {
        return main;
    }

    public List<Address> getAdditional() {
        return additional;
    }

    public List<Address> getVerified() {
        return verified;
    }

    @Override
    public Iterator<Address> iterator() {
        // return 3 iterators main and additional and verified like one?
        return additional.iterator() + verified.iterator() + Collections.singletonList(main).iterator();
    }

}

I just need to iterate through all items in both lists and the main address in one cycle but have them separated. For example, I am going to use it like this

User user = ...
Addresses addresses = new Addresses(user.getMainAddress(), user.getAdditionalAddressess(), user.getVerifiedAddressess());
//iterate through addresses and change some fields an each address
user.setMain(addresses.getMain());
user.setAdditional(addresses.getAdditional());
user.setVerified(addresses.getVerified());

Solution

  • Use Stream.concat, and obtain an Iterator from the resulting Stream:

    public Iterator<Address> iterator() {
        return Stream.concat(
            Stream.concat(additional.stream(), verified.stream()),
            Stream.of(main)).iterator();
    }
    

    Be warned, if someone changes the additional or verified addresses while the Iterator is still being used, the results are “undefined.” You can just document that in your class javadoc, or you can copy the Lists, so your method provides a snapshot and is unaffected by later changes:

    public Iterator<Address> iterator() {
        Stream<String> additionalStream = new ArrayList<>(additional).stream();
        Stream<String> verifiedStream = new ArrayList<>(verified).stream();
        return Stream.concat(
            Stream.concat(additionalStream, verifiedStream),
            Stream.of(main)).iterator();
    }