Combinez plusieurs collections en une seule collection logique?

Supposons que je possède 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’ils puissent simplement parcourir tous les éléments (idéalement, en lecture seule). J’utilise des collections de goyaves et je me demande comment utiliser les itérations / itérations de goyave pour générer une vue logique des collections internes sans faire de copies temporaires.

Avec Guava, vous pouvez utiliser Iterables.concat(Iterable ...) , il crée une vue en direct de toutes les itérables, concaténées en une seule (si vous modifiez les itérables, la version concaténée change également). Enroulez ensuite l’itérable concaténé avec Iterables.unmodifiableIterable(Iterable) (je n’avais pas vu l’exigence de lecture seule plus tôt).

A partir des Iterables.concat( .. ) JavaDocs:

Combine plusieurs itérables en un seul itérable. L’itérable retourné a un iterator qui parcourt les éléments de chaque entrée itérable. Les iterators d’entrée ne sont interrogés que lorsque cela est nécessaire. L’iterator de l’itérable renvoyé prend en charge remove() lorsque l’iterator 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 que c’est le cas (en prenant en charge la méthode Iterator.remove() uniquement si l’iterator de sauvegarde ne le permet pas, sauf en utilisant une vue en direct)

Exemple de code:

 final List first = Lists.newArrayList(1, 2, 3); final List second = Lists.newArrayList(4, 5, 6); final List third = Lists.newArrayList(7, 8, 9); final Iterable 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:

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

 public final class CollectionsX { static class JoinedCollectionView implements Collection { 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 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[] 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. * 

* All methods except {@link Collection#size()}, {@link Collection#clear()}, * {@link Collection#isEmpty()} and {@link Iterable#iterator()} * throw {@link UnsupportedOperationException} in the returned Collection. *

* None of the above methods is thread safe (nor would there be an easy way * of making them). */ public static Collection combine( final Collection< ? extends T>... items) { return new JoinedCollectionView(items); } private CollectionsX() { } }

Plain Java 8 solutions utilisant un Stream .

Nombre constant

En supposant private Collection c, c2, c3 .

Une solution:

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

Une autre solution:

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

Nombre variable

En supposant private Collection> cs :

 public Stream stream() { return cs.stream().flatMap(Collection::stream); } 

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

Si vous utilisez déjà Google Guava, consultez la réponse de Sean Pasortingck Floyd .

Si vous êtes bloqué à Java 7 et que vous ne voulez pas inclure Google Guava, vous pouvez écrire votre propre (lecture seule) Iterables.concat() utilisant uniquement Iterable et Iterator :

Nombre constant

 public static  Iterable concat(final Iterable< ? extends E> iterable1, final Iterable< ? extends E> iterable2) { return new Iterable() { @Override public Iterator iterator() { return new Iterator() { 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  Iterable concat(final Iterable< ? extends E>... iterables) { return concat(Arrays.asList(iterables)); } public static  Iterable concat(final Iterable> iterables) { return new Iterable() { final Iterator> iterablesIterator = iterables.iterator(); @Override public Iterator iterator() { return !iterablesIterator.hasNext() ? Collections.emptyIterator() : new Iterator() { 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 findNext() { while (!iterableIterator.hasNext()) { if (!iterablesIterator.hasNext()) { break; } iterableIterator = nextIterator(); } return this; } }.findNext(); } }; } 

Vous pouvez créer une nouvelle List et addAll() vos autres List . Ensuite, retournez une liste non modifiable avec Collections.unmodifiableList() .

Voici ma solution pour cela:

EDIT – a changé un peu le code

 public static  Iterable concat(final Iterable< ? extends E> list1, Iterable< ? extends E> list2) { return new Iterable() { public Iterator iterator() { return new Iterator() { 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(); } }; } }; }