web-dev-qa-db-fra.com

Un moyen facile de convertir Iterable en Collection

Dans mon application, j'utilise une bibliothèque tierce (Spring Data pour MongoDB pour être exact).

Les méthodes de cette bibliothèque renvoient _Iterable<T>_, alors que le reste de mon code attend _Collection<T>_.

Existe-t-il une méthode d’utilité quelque part qui me permette de convertir rapidement l’un en l’autre? Je voudrais éviter de créer un tas de foreach boucles dans mon code pour une chose aussi simple.

384
Ula Krukar

Avec Guava , vous pouvez utiliser Lists.newArrayList (Iterable) ou Sets.newHashSet (Iterable) , parmi d'autres méthodes similaires. Cela va bien sûr copier tous les éléments dans la mémoire. Si cela n’est pas acceptable, je pense que votre code qui fonctionne avec ceux-ci doit prendre Iterable plutôt que Collection. Guava fournit également des méthodes pratiques pour effectuer des tâches que vous pouvez effectuer sur un Collection en utilisant un Iterable (tel que Iterables.isEmpty(Iterable) ou Iterables.contains(Iterable, Object)), mais les conséquences sur les performances sont plus évidentes. .

363
ColinD

Dans JDK 8, sans dépendre de bibliothèques supplémentaires:

Iterator<T> source = ...;
List<T> target = new ArrayList<>();
source.forEachRemaining(target::add);

Edit: Celui ci-dessus est pour Iterator. Si vous avez affaire à Iterable,

iterable.forEach(target::add);
331
Thamme Gowda

Vous pouvez aussi écrire votre propre méthode d’utilité:

public static <E> Collection<E> makeCollection(Iterable<E> iter) {
    Collection<E> list = new ArrayList<E>();
    for (E item : iter) {
        list.add(item);
    }
    return list;
}
88
Atreys

Solution concise avec Java 8 en utilisant Java.util.stream :

_public static <T> List<T> toList(final Iterable<T> iterable) {
    return StreamSupport.stream(iterable.spliterator(), false)
                        .collect(Collectors.toList());
}
_
71
xehpuk

IteratorUtils de commons-collections peut être utile (bien qu'ils ne prennent pas en charge les génériques de la dernière version stable 3.2.1):

@SuppressWarnings("unchecked")
Collection<Type> list = IteratorUtils.toList(iterable.iterator());

La version 4.0 (qui est dans SNAPSHOT en ce moment) prend en charge les génériques et vous pouvez vous débarrasser du @SuppressWarnings.

Mise à jour: Vérifiez IterableAsList de Cactoos .

45
yegor256

De CollectionUtils :

List<T> targetCollection = new ArrayList<T>();
CollectionUtils.addAll(targetCollection, iterable.iterator())

Voici les sources complètes de cette méthode d'utilitaire:

public static <T> void addAll(Collection<T> collection, Iterator<T> iterator) {
    while (iterator.hasNext()) {
        collection.add(iterator.next());
    }
}
19
Tomasz Nurkiewicz

J'utilise beaucoup FluentIterable.from(myIterable).toList().

14
fringd

N'oubliez pas que toutes les collections sont finies, alors qu'Iterable n'a aucune promesse. Si quelque chose est Iterable, vous pouvez obtenir un Iterator et c'est tout.

for (piece : sthIterable){
..........
}

sera étendu à:

Iterator it = sthIterable.iterator();
while (it.hasNext()){
    piece = it.next();
..........
}

it.hasNext () n'est pas obligé de toujours retourner false. Ainsi, dans le cas général, vous ne pouvez pas vous attendre à pouvoir convertir chaque Iterable en une collection. Par exemple, vous pouvez parcourir tous les nombres naturels positifs, effectuer une itération contenant des cycles qui produisent les mêmes résultats, etc.

Sinon: la réponse d'Atrey est plutôt bonne.

14
Alexander Shopov

Ce n'est pas une réponse à votre question mais je pense que c'est la solution à votre problème. L'interface org.springframework.data.repository.CrudRepository a effectivement des méthodes qui retournent Java.lang.Iterable mais vous ne devez pas utiliser cette interface. Utilisez plutôt des sous-interfaces, dans votre cas org.springframework.data.mongodb.repository.MongoRepository. Cette interface a des méthodes qui renvoient des objets de type Java.util.List.

8
Ludwig Magnusson

J'utilise mon utilitaire personnalisé pour convertir une collection existante, le cas échéant.

Main:

public static <T> Collection<T> toCollection(Iterable<T> iterable) {
    if (iterable instanceof Collection) {
        return (Collection<T>) iterable;
    } else {
        return Lists.newArrayList(iterable);
    }
}

Idéalement, ce qui précède utiliserait ImmutableList, mais ImmutableCollection n'autorise pas les valeurs NULL qui peuvent donner des résultats indésirables.

Tests:

@Test
public void testToCollectionAlreadyCollection() {
    ArrayList<String> list = Lists.newArrayList(FIRST, MIDDLE, LAST);
    assertSame("no need to change, just cast", list, toCollection(list));
}

@Test
public void testIterableToCollection() {
    final ArrayList<String> expected = Lists.newArrayList(FIRST, null, MIDDLE, LAST);

    Collection<String> collection = toCollection(new Iterable<String>() {
        @Override
        public Iterator<String> iterator() {
            return expected.iterator();
        }
    });
    assertNotSame("a new list must have been created", expected, collection);
    assertTrue(expected + " != " + collection, CollectionUtils.isEqualCollection(expected, collection));
}

J'implémente des utilitaires similaires pour tous les sous-types de collections (Set, List, etc.). Je pense que ceux-ci feraient déjà partie de la goyave, mais je ne l'ai pas trouvée.

7
Aaron Roller

Dans Java 8, vous pouvez ajouter tous les éléments d’un Iterable à Collection et le renvoyer:

public static <T> Collection<T> iterableToCollection(Iterable<T> iterable) {
  Collection<T> collection = new ArrayList<>();
  iterable.forEach(collection::add);
  return collection;
}

Inspiré par la réponse de @Afreys.

5
user1019830

Dès que vous appelez contains, containsAll, equals, hashCode, remove, retainAll, size ou toArray, il faudrait quand même traverser les éléments.

Si vous appelez occasionnellement des méthodes telles que isEmpty ou clear, je suppose que vous feriez mieux de créer la collection paresseusement. Vous pouvez par exemple avoir un support ArrayList pour stocker des éléments précédemment itérés.

Je ne connais aucun de ces cours dans une bibliothèque, mais ce devrait être un exercice assez simple à rédiger.

5
aioobe

Puisque RxJava est un marteau et que ça ressemble un peu à un clou, tu peux le faire

Observable.from(iterable).toList().toBlocking().single();
5
DariusL

Voici un SSCCE pour un excellent moyen de faire cela dans Java 8

import Java.util.ArrayList;
import Java.util.Collection;
import Java.util.HashSet;
import Java.util.LinkedList;
import Java.util.stream.Collectors;
import Java.util.stream.IntStream;

public class IterableToCollection {
    public interface CollectionFactory <T, U extends Collection<T>> {
        U createCollection();
    }

    public static <T, U extends Collection<T>> U collect(Iterable<T> iterable, CollectionFactory<T, U> factory) {
        U collection = factory.createCollection();
        iterable.forEach(collection::add);
        return collection;
    }

    public static void main(String[] args) {
        Iterable<Integer> iterable = IntStream.range(0, 5).boxed().collect(Collectors.toList());
        ArrayList<Integer> arrayList = collect(iterable, ArrayList::new);
        HashSet<Integer> hashSet = collect(iterable, HashSet::new);
        LinkedList<Integer> linkedList = collect(iterable, LinkedList::new);
    }
}
3
michaelsnowden

Deux remarques

  1. Il n'est pas nécessaire de convertir Iterable en Collection pour utiliser foreach boucle - Iterable peut être utilisé directement dans une telle boucle. Il n'y a pas de différence syntaxique. Je ne comprends donc pas pourquoi la question initiale a été posée.
  2. La méthode suggérée pour convertir Iterable en Collection est dangereuse (le même principe s'applique à CollectionUtils) - rien ne garantit que les appels ultérieurs à la méthode next () renvoient des instances d'objet différentes. De plus, cette préoccupation n’est pas purement théorique. Par exemple. L'implémentation itérative utilisée pour transmettre des valeurs à une méthode de réduction de Hadoop Reducer renvoie toujours la même instance de valeur, mais avec des valeurs de champ différentes. Donc, si vous appliquez makeCollection d'en haut (ou CollectionUtils.addAll (Iterator)), vous obtiendrez une collection avec tous les éléments identiques.
2
al0

Essayez StickyList de Cactoos :

List<String> list = new StickyList<>(iterable);
1
yegor256