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.
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. .
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);
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;
}
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());
}
_
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 .
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());
}
}
J'utilise beaucoup FluentIterable.from(myIterable).toList()
.
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.
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
.
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.
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.
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.
Puisque RxJava est un marteau et que ça ressemble un peu à un clou, tu peux le faire
Observable.from(iterable).toList().toBlocking().single();
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);
}
}
Deux remarques
Essayez StickyList
de Cactoos :
List<String> list = new StickyList<>(iterable);