Combine multiple Collections into a single logical Collection?

newgre picture newgre · Feb 4, 2011 · Viewed 111.4k times · Source

Assume, I have a constant number of collections (e.g. 3 ArrayLists) as members of a class. Now, I want to expose all the elements to other classes so they can simply iterate over all elements (ideally, read only). I'm using guava collections and I wonder how I could use guava iterables/iterators to generate a logical view on the internal collections without making temporary copies.

Answer

Sean Patrick Floyd picture Sean Patrick Floyd · Feb 4, 2011

With Guava, you can use Iterables.concat(Iterable<T> ...), it creates a live view of all the iterables, concatenated into one (if you change the iterables, the concatenated version also changes). Then wrap the concatenated iterable with Iterables.unmodifiableIterable(Iterable<T>) (I hadn't seen the read-only requirement earlier).

From the Iterables.concat( .. ) JavaDocs:

Combines multiple iterables into a single iterable. The returned iterable has an iterator that traverses the elements of each iterable in inputs. The input iterators are not polled until necessary. The returned iterable's iterator supports remove() when the corresponding input iterator supports it.

While this doesn't explicitly say that this is a live view, the last sentence implies that it is (supporting the Iterator.remove() method only if the backing iterator supports it is not possible unless using a live view)

Sample Code:

final List<Integer> first  = Lists.newArrayList(1, 2, 3);
final List<Integer> second = Lists.newArrayList(4, 5, 6);
final List<Integer> third  = Lists.newArrayList(7, 8, 9);
final Iterable<Integer> all =
    Iterables.unmodifiableIterable(
        Iterables.concat(first, second, third));
System.out.println(all);
third.add(9999999);
System.out.println(all);

Output:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 9999999]


Edit:

By Request from Damian, here's a similar method that returns a live Collection View

public final class CollectionsX {

    static class JoinedCollectionView<E> implements Collection<E> {

        private final Collection<? extends E>[] items;

        public JoinedCollectionView(final Collection<? extends E>[] items) {
            this.items = items;
        }

        @Override
        public boolean addAll(final Collection<? extends E> c) {
            throw new UnsupportedOperationException();
        }

        @Override
        public void clear() {
            for (final Collection<? extends E> coll : items) {
                coll.clear();
            }
        }

        @Override
        public boolean contains(final Object o) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean containsAll(final Collection<?> c) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean isEmpty() {
            return !iterator().hasNext();
        }

        @Override
        public Iterator<E> iterator() {
            return Iterables.concat(items).iterator();
        }

        @Override
        public boolean remove(final Object o) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean removeAll(final Collection<?> c) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean retainAll(final Collection<?> c) {
            throw new UnsupportedOperationException();
        }

        @Override
        public int size() {
            int ct = 0;
            for (final Collection<? extends E> coll : items) {
                ct += coll.size();
            }
            return ct;
        }

        @Override
        public Object[] toArray() {
            throw new UnsupportedOperationException();
        }

        @Override
        public <T> T[] toArray(T[] a) {
            throw new UnsupportedOperationException();
        }

        @Override
        public boolean add(E e) {
            throw new UnsupportedOperationException();
        }

    }

    /**
     * Returns a live aggregated collection view of the collections passed in.
     * <p>
     * All methods except {@link Collection#size()}, {@link Collection#clear()},
     * {@link Collection#isEmpty()} and {@link Iterable#iterator()}
     *  throw {@link UnsupportedOperationException} in the returned Collection.
     * <p>
     * None of the above methods is thread safe (nor would there be an easy way
     * of making them).
     */
    public static <T> Collection<T> combine(
        final Collection<? extends T>... items) {
        return new JoinedCollectionView<T>(items);
    }

    private CollectionsX() {
    }

}