Modo semplice per convertire Iterable in Collection


424

Nella mia applicazione utilizzo una libreria di terze parti (i dati di primavera per MongoDB per l'esattezza).

I metodi di questa libreria ritornano Iterable<T>, mentre il resto del mio codice si aspettaCollection<T> .

Esiste un metodo di utilità da qualche parte che mi permetta di convertire rapidamente l'uno nell'altro? Vorrei evitare di creare un mucchio di foreachloop nel mio codice per una cosa così semplice.


3
Qualsiasi metodo utile per eseguire l'operazione è comunque legato all'iterazione della raccolta, quindi non ci si può aspettare alcun miglioramento delle prestazioni. Ma se stai solo cercando zucchero sintattico, sceglierei Guava o forse le collezioni Apache.
Sebastian Ganslandt,

" è comunque legato all'iterazione della collezione ", - no, non lo è. Vedi la mia risposta per i dettagli.
aioobe,

3
nel tuo specifico caso d'uso, potresti semplicemente estendere CrudRepository con la tua interfaccia con metodi che restituiscono Collection <T> / List <T> / Set <T> (se necessario) invece di Iterable <T>
Kevin Van Dyck

Risposte:


387

Con Guava puoi usare Lists.newArrayList (Iterable) o Sets.newHashSet (Iterable) , tra altri metodi simili. Questo ovviamente copierà tutti gli elementi in memoria. Se questo non è accettabile, penso che il tuo codice che funziona con questi dovrebbe prendere Iterablepiuttosto che Collection. Guava fornisce anche metodi convenienti per fare cose che puoi fare Collectionsull'uso di Iterable(come Iterables.isEmpty(Iterable)o Iterables.contains(Iterable, Object)), ma le implicazioni sulle prestazioni sono più ovvie.


1
Esegue l'iterazione diretta di tutti gli elementi? Cioè, è Lists.newArrayList(Iterable).clear()un'operazione a tempo lineare o costante?
aioobe,

2
@aioobe: crea una copia dell'iterabile. Non è stato specificato che era desiderata una vista e dato che la maggior parte dei metodi su Collectionuno non può essere implementato per una vista di uno Iterableo non sarà efficiente, per me non ha molto senso farlo.
ColinD,

@ColinD e se volessi una vista? In realtà, quello che voglio è una vista Raccolta che è il risultato dell'aggiunta di una raccolta sorgente con un altro elemento. Posso usare Iterables.concat()ma questo dà un Iterable, non un Collection:(
Hendy Irawan

1
Questa è la mia domanda: stackoverflow.com/questions/4896662/… . Sfortunatamente la semplice risposta che non risolve il problema è l'utilizzo Iterables.concat(). La risposta molto più lunga dà Collection... Mi chiedo perché questo non è più comunemente supportato?
Hendy Irawan,

365

In JDK 8+, senza utilizzare librerie aggiuntive:

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

Modifica: quello sopra è per Iterator. Se hai a che fare Iterable,

iterable.forEach(target::add);

86
Oppureiterable.forEach(target::add);
Cefalopode,

92

Puoi anche scrivere il tuo metodo di utilità per questo:

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

33
+1 Se passare da Iterablea Collectionè l'unica preoccupazione, preferirei questo approccio piuttosto che importare una grande libreria di raccolte di terze parti.
aioobe,

2
4 righe di codice funzione sono molto più preferibili rispetto a 2 MB di codice di libreria compilato per il quale il 99% non viene utilizzato. C'è un altro costo: complicazioni di licenza. La licenza di Apache 2.0 è flessibile, ma non senza alcuni noiosi mandati. Idealmente, vedremmo alcuni di questi schemi comuni integrati direttamente nelle librerie di runtime Java.
Jonathan Neufeld,

2
Un altro punto, dato che stai usando comunque una ArrayList, perché non semplicemente scegliere il tipo Elenco covariante? Ciò consente di soddisfare più contratti senza down-casting o ricomposizione e Java non ha comunque supporto per limiti di tipo inferiori.
Jonathan Neufeld,

@JonathanNeufeld o perché non andare avanti e restituire un ArrayList <T>?
Juan,

5
@Juan Perché non è molto SOLIDO . Una ArrayList espone dettagli di implementazione che sono probabilmente inutili (YAGNI), il che viola i singoli principi di inversione di responsabilità e dipendenza. Lo lascerei a List perché espone un po 'più di Collection, rimanendo completamente SOLIDO. Se sei preoccupato per l'impatto sulle prestazioni JVM dell'opcode INVOKEINTERFACE su INVOKEVIRTUAL, molti benchmark riveleranno che non vale la pena perdere sonno.
Jonathan Neufeld il

81

Soluzione concisa con Java 8 utilizzando java.util.stream:

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

1
questo approccio è troppo lento rispetto a IteratorUtilsdacommons-collections
Alex Burdusel il

3
Quanto più lento? IteratorUtils.toList()usa l'iteratore in un modo precedente a Java 5 per aggiungere gli elementi uno alla volta a un elenco appena creato. Semplice e forse il più veloce, ma aggiunge 734 kB al tuo binario e potresti farlo da solo se scoprissi che questo metodo è il migliore.
Xehpuk,

8
Ho fatto un benchmark primitivo concludendo che a volte il primo è più veloce, a volte il secondo è più veloce. Mostraci il tuo punto di riferimento.
xehpuk,

questa prob potrebbe essere una nuova risposta accettata - È bello evitare lib lib in eccesso (come Guava).
java-addict301

48

IteratorUtilsda commons-collectionspuò aiutare (anche se non supportano generici nell'ultima versione stabile 3.2.1):

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

La versione 4.0 (che è in SNAPSHOT in questo momento) supporta generici e puoi sbarazzarti di @SuppressWarnings .

Aggiornamento: controllare IterableAsListda Cactoos .


5
Ma ciò richiede un Iteratore, non un Iterabile
hithwen il

5
@hithwen, non capisco - Iterable fornisce un Iterator (come dettagliato nella risposta) - qual è il problema?
Tom,

Non so cosa stavo pensando ^^ U
hithwen,

2
Dal 4.1 c'è anche IterableUtils.toList(Iterable), che è un metodo conveniente e utilizza IteratorUtilssotto il cofano, ma è anche null-safe (a differenza IteratorUtils.toList).
Yoory N.

21

Da CollectionUtils :

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

Ecco le fonti complete di questo metodo di utilità:

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

Esegue l'iterazione diretta di tutti gli elementi? Cioè, è Lists.newArrayList(someIterable).clear()un'operazione a tempo lineare o costante?
aioobe,

Ho aggiunto il codice sorgente di addAll, come suggerisce il nome, copia i valori dell'iteratore uno dopo l'altro; crea una copia anziché una vista.
Tomasz Nurkiewicz,

È un peccato che non ci sia metodo CollectionUtilsper saltare la creazione della collezione in una linea in più.
Karl Richter,

Broken Link ☝️☝️
Hola Soy Edu Feliz Navidad

14

Durante questo, non dimenticare che tutte le collezioni sono limitate, mentre Iterable non ha promesse di sorta. Se qualcosa è Iterabile puoi ottenere un Iteratore e basta.

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

sarà esteso a:

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

it.hasNext () non è necessario per restituire mai false. Quindi, nel caso generale, non puoi aspettarti di poter convertire ogni Iterable in una Collezione. Ad esempio puoi iterare su tutti i numeri naturali positivi, iterare su qualcosa con cicli in esso che producono gli stessi risultati più e più volte, ecc.

Altrimenti: la risposta di Atrey è abbastanza buona.


1
Qualcuno ha mai effettivamente trovato un Iterable che scorre su qualcosa di infinito (come l'esempio di numeri naturali fornito nella risposta), in pratica / codice reale? Penserei che un simile Iterable provocherebbe dolore e guai in molti posti ... :)
David

2
@David Anche se non posso specificamente indicare un Iteratore infinito in nessuno dei miei codici di produzione, posso pensare ai casi in cui potrebbero verificarsi. Un videogioco potrebbe avere un'abilità che crea elementi nel modello ciclico suggerito dalla risposta sopra. Anche se non ho incontrato infiniti iteratori, ho sicuramente incontrato iteratori in cui la memoria è una vera preoccupazione. Ho iteratori sui file su disco. Se ho un disco da 1 TB pieno e 4 GB di RAM, potrei facilmente esaurire la memoria convertendo il mio iteratore in una raccolta.
radicaledward101,

14

Ne uso FluentIterable.from(myIterable).toList()molto.


9
Va notato che proviene anche dalla Guava.
Vadzim,

O da org.apache.commons.collections4. Quindi è FluentIterable.of (myIterable) .toList ()
du-it

9

Questa non è una risposta alla tua domanda, ma credo che sia la soluzione al tuo problema. L'interfaccia org.springframework.data.repository.CrudRepositoryha davvero dei metodi che ritornano java.lang.Iterablema non dovresti usare questa interfaccia. Usa invece interfacce secondarie, nel tuo caso org.springframework.data.mongodb.repository.MongoRepository. Questa interfaccia ha metodi che restituiscono oggetti di tipo java.util.List.


2
Vorrei promuovere l'uso del generico CrudRepository per evitare di associare il codice a un'implementazione concreta.
stanlick,

7

Uso la mia utility personalizzata per trasmettere una raccolta esistente, se disponibile.

Principale:

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

Idealmente quanto sopra dovrebbe utilizzare ImmutableList, ma ImmutableCollection non consente valori nulli che possono fornire risultati indesiderati.

test:

@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));
}

Implemento utilità simili per tutti i sottotipi di Collezioni (Set, Elenco, ecc.). Penserei che questi sarebbero già parte di Guava, ma non l'ho trovato.


1
La tua risposta di un anno fa da base a una nuova domanda stackoverflow.com/questions/32570534/… che attira molte opinioni e commenti.
Paul Boddington,

6

Non appena si chiami contains, containsAll, equals, hashCode, remove, retainAll, sizeotoArray , che avrebbe dovuto attraversare gli elementi in ogni caso.

Se di tanto in tanto chiami solo metodi come isEmptyo clearsuppongo che sarebbe meglio creare pigramente la raccolta. Ad esempio, potresti avere un sostegnoArrayList per la memorizzazione di elementi precedentemente ripetuti.

Non conosco nessuna di queste classi in nessuna biblioteca, ma dovrebbe essere un esercizio abbastanza semplice da scrivere.



6

In Java 8 puoi farlo per aggiungere tutti gli elementi da un Iterablea Collectione restituirlo:

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

Ispirato dalla risposta @Afreys.


5

Poiché RxJava è un martello e questo tipo di chiodo sembra un chiodo, puoi farlo

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

23
c'è forse un modo per coinvolgere jquery?
Dmitry Minkovsky,

3
si arresta in modo anomalo se è presente un elemento null in RxJava. vero?
MBH,

Credo che RxJava2 non consenta articoli null, dovrebbe andare bene in RxJava.
Dario

4

Ecco un SSCCE per un ottimo modo per farlo in 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);
    }
}

4

Mi sono imbattuto in una situazione simile durante il tentativo di recuperare a Listof Projects, anziché il valore predefinito Iterable<T> findAll()dichiarato CrudRepositorynell'interfaccia. Quindi, nella mia ProjectRepositoryinterfaccia (che si estende da CrudRepository), ho semplicemente dichiarato il findAll()metodo per restituire un List<Project>invece di Iterable<Project>.

package com.example.projectmanagement.dao;

import com.example.projectmanagement.entities.Project;
import org.springframework.data.repository.CrudRepository;
import java.util.List;

public interface ProjectRepository extends CrudRepository<Project, Long> {

    @Override
    List<Project> findAll();
}

Questa è la soluzione più semplice, credo, senza richiedere la logica di conversione o l'utilizzo di librerie esterne.


2

Due osservazioni

  1. Non è necessario convertire Iterable in Collection per utilizzare foreach loop - Iterable può essere utilizzato direttamente in tale loop, non esiste alcuna differenza sintattica, quindi non capisco perché la domanda originale sia stata posta.
  2. Il modo suggerito per convertire Iterable in Collection non è sicuro (lo stesso si riferisce a CollectionUtils) - non vi è alcuna garanzia che le chiamate successive al metodo next () restituiscano istanze di oggetti diverse. Inoltre, questa preoccupazione non è pura teoria. Ad esempio l'implementazione Iterable utilizzata per passare valori a un metodo di riduzione di Hadoop Reducer restituisce sempre la stessa istanza di valore, solo con valori di campo diversi. Quindi se applichi makeCollection dall'alto (o CollectionUtils.addAll (Iteratore)) finirai con una raccolta con tutti gli elementi identici.


1

Non ho visto una soluzione a una riga semplice senza dipendenze. Io uso semplice

List<Users> list;
Iterable<IterableUsers> users = getUsers();

// one line solution
list = StreamSupport.stream(users.spliterator(), true).collect(Collectors.toList());

0

Puoi utilizzare le fabbriche di Eclipse Collections :

Iterable<String> iterable = Arrays.asList("1", "2", "3");

MutableList<String> list = Lists.mutable.withAll(iterable);
MutableSet<String> set = Sets.mutable.withAll(iterable);
MutableSortedSet<String> sortedSet = SortedSets.mutable.withAll(iterable);
MutableBag<String> bag = Bags.mutable.withAll(iterable);
MutableSortedBag<String> sortedBag = SortedBags.mutable.withAll(iterable);

Puoi anche convertire il file Iterablein a LazyIterablee utilizzare i metodi di conversione o una qualsiasi delle altre API disponibili disponibili.

Iterable<String> iterable = Arrays.asList("1", "2", "3");
LazyIterable<String> lazy = LazyIterate.adapt(iterable);

MutableList<String> list = lazy.toList();
MutableSet<String> set = lazy.toSet();
MutableSortedSet<String> sortedSet = lazy.toSortedSet();
MutableBag<String> bag = lazy.toBag();
MutableSortedBag<String> sortedBag = lazy.toSortedBag();

Tutti i Mutabletipi di cui sopra si estendonojava.util.Collection .

Nota: sono un committer per le raccolte Eclipse.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.