Łatwy sposób na konwersję Iterable do kolekcji

W mojej aplikacji używam 3rd party library(Spring Data for MongoDB by być dokładnym).

Metody tej biblioteki zwracają Iterable<T>, podczas gdy reszta mojego kodu oczekuje Collection<T>.

Czy jest gdzieś jakaś metoda użytkowa, która pozwoli mi szybko przekonwertować jedną na drugą? Chciałbym uniknąć tworzenia kilku foreach pętli w moim kodzie dla tak prostej rzeczy.

Author: Martin Schröder, 2011-06-20

19 answers

Z guawa możesz użyć List.zestawy newArrayList (Iterable) lub .newHashSet (Iterable) , wśród innych podobnych metod. To oczywiście skopiuje wszystkie elementy do pamięci. Jeśli to nie jest do przyjęcia, myślę, że Twój kod, który działa z nimi powinien przyjmować Iterable, a nie Collection. Guawa zdarza się również zapewnić wygodne metody robienia rzeczy, które można zrobić na Collection za pomocą Iterable (takie jak Iterables.isEmpty(Iterable) lub Iterables.contains(Iterable, Object)), ale implikacje wydajności są bardziej oczywiste.

 393
Author: ColinD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-01 10:39:23

W JDK 8+, bez użycia dodatkowych libs:

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

Edit: powyższy jest dla Iterator. Jeśli masz do czynienia z Iterable,

iterable.forEach(target::add);
 381
Author: Thamme Gowda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-17 02:14:56

Możesz również napisać własną metodę użytkową:

public static <E> Collection<E> makeCollection(Iterable<E> iter) {
    Collection<E> list = new ArrayList<E>();
    for (E item : iter) {
        list.add(item);
    }
    return list;
}
 96
Author: Atreys,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-27 15:16:43

Zwięzłe rozwiązanie z Java 8 za pomocą java.util.stream:

public static <T> List<T> toList(final Iterable<T> iterable) {
    return StreamSupport.stream(iterable.spliterator(), false)
                        .collect(Collectors.toList());
}
 91
Author: xehpuk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-02 14:25:40

IteratorUtils z commons-collections mogą pomóc (chociaż nie obsługują generyków w najnowszej stabilnej wersji 3.2.1):

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

Wersja 4.0 (która jest w tej chwili w migawce) obsługuje generyki i możesz pozbyć się @SuppressWarnings.

Aktualizacja: sprawdź IterableAsList z Cactoos .

 49
Author: yegor256,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-10 09:12:36

From Collection :

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

Oto pełne źródła tej metody użytkowej:

public static <T> void addAll(Collection<T> collection, Iterator<T> iterator) {
    while (iterator.hasNext()) {
        collection.add(iterator.next());
    }
}
 23
Author: Tomasz Nurkiewicz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-30 20:22:01

Nie zapominaj, że wszystkie zbiory są skończone, podczas gdy Iterable nie ma żadnych obietnic. Jeśli coś jest iteracyjne można uzyskać Iterator i to jest to.

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

Zostanie rozszerzona do:

Iterator it = sthIterable.iterator();
while (it.hasNext()){
    piece = it.next();
..........
}
To.hasNext () nie jest wymagane, aby kiedykolwiek zwrócić false. Tak więc w ogólnym przypadku nie można oczekiwać, aby być w stanie przekonwertować każdy Iterable do zbioru. Na przykład można iterację nad wszystkimi dodatnimi liczbami naturalnymi, iterację nad czymś z cyklami w nim, które wytwarzają te same wyniki w kółko, itp.

Inaczej: odpowiedź Atreya jest całkiem dobra.

 14
Author: Alexander Shopov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-07 20:11:26

Używam FluentIterable.from(myIterable).toList() dużo.

 14
Author: fringd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-18 23:11:21

Natknąłem się na podobną sytuację, próbując pobrać List z Project s, zamiast domyślnego Iterable<T> findAll() zadeklarowanego w CrudRepository interfejsie. Tak więc, w moim interfejsie ProjectRepository (który rozciąga się od CrudRepository), po prostu zadeklarowałem metodę findAll(), aby zwrócić List<Project> zamiast 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();
}

Jest to chyba najprostsze rozwiązanie, nie wymagające logiki konwersji ani użycia zewnętrznych bibliotek.

 12
Author: Manish Giri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-30 04:14:53

To nie jest odpowiedź na twoje pytanie, ale uważam, że jest to rozwiązanie twojego problemu. Interfejs org.springframework.data.repository.CrudRepository rzeczywiście posiada metody zwracające java.lang.Iterable, ale nie powinieneś używać tego interfejsu. Zamiast tego użyj interfejsów podrzędnych, w Twoim przypadku org.springframework.data.mongodb.repository.MongoRepository. Interfejs ten posiada metody zwracające obiekty typu java.util.List.

 9
Author: Ludwig Magnusson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-06-18 12:04:08

Używam mojego niestandardowego narzędzia do tworzenia istniejącej kolekcji, jeśli jest dostępna.

Główna:

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

Najlepiej byłoby użyć ImmutableList, ale ImmutableCollection nie pozwala na NULL, które mogą przynieść niepożądane rezultaty.

Testy:

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

Implementuję podobne narzędzia dla wszystkich podtypów kolekcji (Set,List,etc). Myślałem, że to już część guawy, ale jej nie znalazłem.

 7
Author: Aaron Roller,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-04 08:35:13

Jak tylko zadzwonisz contains, containsAll, equals, hashCode, remove, retainAll, size albo toArray, i tak musiałbyś przejść przez żywioły.

Jeśli od czasu do czasu wywołujesz tylko metody takie jak isEmpty lub clear, to myślę, że lepiej będzie, tworząc kolekcję leniwie. Możesz na przykład mieć Backup ArrayList do przechowywania wcześniej iteracyjnych elementów.

Nie znam żadnej takiej klasy w żadnej bibliotece, ale powinno to być dość proste ćwiczenie do napisania.

 6
Author: aioobe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-20 20:29:35

W Javie 8 możesz to zrobić, aby dodać wszystkie elementy z Iterable do Collection i zwrócić je:

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

Zainspirowany przez @ Afreys odpowiedz.

 6
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-24 14:05:37

Ponieważ RxJava jest młotkiem, a ten wygląda jak gwóźdź, możesz to zrobić

Observable.from(iterable).toList().toBlocking().single();
 5
Author: DariusL,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-15 07:57:52

Oto SSCCE na świetny sposób, aby to zrobić w Javie 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
Author: michaelsnowden,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-08 06:54:30

Dwie uwagi

  1. nie ma potrzeby konwertowania iterable do kolekcji, aby używać foreach loop - Iterable może być użyty w takiej pętli bezpośrednio, nie ma różnica składniowa, więc trudno zrozumieć, dlaczego w ogóle zadano pierwotne pytanie.
  2. sugerowany sposób konwersji Iterowalnej do kolekcji jest niebezpieczny (to samo dotyczy CollectionUtils) - nie ma gwarancji, że kolejne wywołania metody next () zwrócą różne instancje obiektów. Co więcej, ta troska nie jest czysta teoria. Np. implementacja Iterable używana do przekazywania wartości do metody reduction reduktora Hadoop zawsze zwraca tę samą instancję wartości, tylko z różnymi wartościami pól. Więc jeśli zastosujesz makeCollection z góry(lub CollectionUtils.addAll (Iterator)) otrzymasz kolekcję ze wszystkimi identycznymi elementami.
 2
Author: al0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-20 12:15:37

Try StickyList from Cactoos :

List<String> list = new StickyList<>(iterable);
 1
Author: yegor256,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-20 17:35:49

Nie widziałem prostego rozwiązania jednowierszowego bez żadnych zależności. I proste użycie

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

// one line solution
list = StreamSupport.stream(users.spliterator(), true).collect(Collectors.toList());
 1
Author: FarukT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-04-27 07:40:01

Możesz użyć kolekcji Eclipse :

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

Możesz również przekonwertować {[2] } na LazyIterable i użyć metod konwertera lub dowolnego innego dostępnego API.

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();

Wszystkie powyższe typy Mutable rozszerzają java.util.Collection.

Uwaga: jestem committerem Dla Kolekcji Eclipse.

 0
Author: Donald Raab,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-01-21 21:53:30