web-dev-qa-db-fra.com

Combiner plusieurs collections en une seule collection logique?

Supposons que j'ai un nombre constant de collections (par exemple 3 ArrayLists) en tant que membres d'une classe. Maintenant, je veux exposer tous les éléments à d'autres classes afin qu'elles puissent simplement parcourir tous les éléments (idéalement, en lecture seule). J'utilise des collections de goyave et je me demande comment je pourrais utiliser des iterables/iterators de goyave pour générer une vue logique sur les collections internes sans faire des copies temporaires.

106
newgre

Avec Guava, vous pouvez utiliser Iterables.concat(Iterable<T> ...) , il crée une vue en direct de tous les itérables, concaténés en un seul (si vous modifiez les itérables, la version concaténée change également). Puis encapsulez l'itération concaténée avec Iterables.unmodifiableIterable(Iterable<T>) (je n'avais pas vu la condition de lecture seule auparavant).

De la Iterables.concat( .. ) JavaDocs:

Combine plusieurs iterables en un seul iterable. L'itéré retourné a un itérateur qui traverse les éléments de chaque itérable dans les entrées. Les itérateurs d’entrée ne sont interrogés que lorsque cela est nécessaire. L'itérateur de l'itéré retourné prend en charge remove() lorsque l'itérateur d'entrée correspondant le prend en charge.

Bien que cela ne dise pas explicitement qu'il s'agit d'une vue en direct, la dernière phrase implique qu'elle est (prise en charge de la méthode Iterator.remove() ) uniquement si l'itérateur qui le prend en charge n'est pas possible sauf si en utilisant une vue en direct)

Exemple de 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);

Sortie:

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


Modifier:

À la demande de Damian, voici une méthode similaire qui renvoie une vue de collection en direct

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

}
111
Sean Patrick Floyd

Plain Java 8 solutions utilisant un Stream .

Nombre constant

En supposant private Collection<T> c, c2, c3.

Une solution:

public Stream<T> stream() {
    return Stream.concat(Stream.concat(c.stream(), c2.stream()), c3.stream());
}

Une autre solution:

public Stream<T> stream() {
    return Stream.of(c, c2, c3).flatMap(Collection::stream);
}

Nombre variable

En supposant private Collection<Collection<T>> cs:

public Stream<T> stream() {
    return cs.stream().flatMap(Collection::stream);
}
98
xehpuk

Si vous utilisez au moins Java 8, voir mon autre réponse .

Si vous utilisez déjà Google Guava, voir réponse de Sean Patrick Floyd .

Si vous êtes bloqué à Java 7 et que vous ne souhaitez pas inclure Google Guava, vous pouvez écrire votre propre (en lecture seule) Iterables.concat()] en n'utilisant pas plus de Iterable et Iterator:

Nombre constant

public static <E> Iterable<E> concat(final Iterable<? extends E> iterable1,
                                     final Iterable<? extends E> iterable2) {
    return new Iterable<E>() {
        @Override
        public Iterator<E> iterator() {
            return new Iterator<E>() {
                final Iterator<? extends E> iterator1 = iterable1.iterator();
                final Iterator<? extends E> iterator2 = iterable2.iterator();

                @Override
                public boolean hasNext() {
                    return iterator1.hasNext() || iterator2.hasNext();
                }

                @Override
                public E next() {
                    return iterator1.hasNext() ? iterator1.next() : iterator2.next();
                }
            };
        }
    };
}

Nombre variable

@SafeVarargs
public static <E> Iterable<E> concat(final Iterable<? extends E>... iterables) {
    return concat(Arrays.asList(iterables));
}

public static <E> Iterable<E> concat(final Iterable<Iterable<? extends E>> iterables) {
    return new Iterable<E>() {
        final Iterator<Iterable<? extends E>> iterablesIterator = iterables.iterator();

        @Override
        public Iterator<E> iterator() {
            return !iterablesIterator.hasNext() ? Collections.emptyIterator()
                                                : new Iterator<E>() {
                Iterator<? extends E> iterableIterator = nextIterator();

                @Override
                public boolean hasNext() {
                    return iterableIterator.hasNext();
                }

                @Override
                public E next() {
                    final E next = iterableIterator.next();
                    findNext();
                    return next;
                }

                Iterator<? extends E> nextIterator() {
                    return iterablesIterator.next().iterator();
                }

                Iterator<E> findNext() {
                    while (!iterableIterator.hasNext()) {
                        if (!iterablesIterator.hasNext()) {
                            break;
                        }
                        iterableIterator = nextIterator();
                    }
                    return this;
                }
            }.findNext();
        }
    };
}
10
xehpuk

Voici ma solution pour cela:

EDIT - a changé un peu le code

public static <E> Iterable<E> concat(final Iterable<? extends E> list1, Iterable<? extends E> list2)
{
    return new Iterable<E>()
    {
        public Iterator<E> iterator()
        {
            return new Iterator<E>()
            {
                protected Iterator<? extends E> listIterator = list1.iterator();
                protected Boolean checkedHasNext;
                protected E nextValue;
                private boolean startTheSecond;

                public void theNext()
                {
                    if (listIterator.hasNext())
                    {
                        checkedHasNext = true;
                        nextValue = listIterator.next();
                    }
                    else if (startTheSecond)
                        checkedHasNext = false;
                    else
                    {
                        startTheSecond = true;
                        listIterator = list2.iterator();
                        theNext();
                    }
                }

                public boolean hasNext()
                {
                    if (checkedHasNext == null)
                        theNext();
                    return checkedHasNext;
                }

                public E next()
                {
                    if (!hasNext())
                        throw new NoSuchElementException();
                    checkedHasNext = null;
                    return nextValue;

                }

                public void remove()
                {
                    listIterator.remove();
                }
            };
        }
    };
}
0
chmouel kalifa

Vous pouvez créer un nouveau List et addAll() de votre autre Lists. Retournez ensuite une liste non modifiable avec Collections.unmodifiableList().

0
Qwerky