Простой способ конвертировать Iterable в коллекцию

В моем приложении я использую стороннюю библиотеку (точнее Spring Data для MongoDB).

Методы этой библиотеки возвращают Iterable<T>, в то время как остальная часть моего кода ожидает Collection<T>.

Есть ли где-нибудь полезный метод, который позволит мне быстро преобразовать один в другой? Я бы хотел не создавать в своем коде кучу циклов foreach для такой простой вещи.

Ответ 1

С Guava вы можете использовать Lists.newArrayList(Iterable) или Sets.newHashSet(Iterable), среди других подобных методов. Это, конечно же, скопирует все элементы в память. Если это неприемлемо, я думаю, что ваш код, который работает с ними, должен принимать Iterable, а не Collection. Кроме того, Guava предоставляет удобные методы для выполнения действий с Collection с помощью Iterable (например, Iterables.isEmpty(Iterable) или Iterables.contains(Iterable, Object)), но последствия производительности более очевидны.

Ответ 2

В JDK 8, не зависимо от дополнительных libs:

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

Изменить: приведенный выше для Iterator. Если вы имеете дело с Iterable,

iterable.forEach(target::add);

Ответ 3

Вы также можете написать свой собственный метод утилиты:

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

Ответ 4

Краткое решение с Java 8 с помощью java.util.stream:

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

Ответ 5

IteratorUtils от commons-collections может помочь (хотя они не поддерживают дженерики в последней стабильной версии 3.2.1):

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

Версия 4.0 (которая находится в SNAPSHOT в данный момент) поддерживает дженерики, и вы можете избавиться от @SuppressWarnings.

Обновление: выберите IterableAsList из Cactoos.

Ответ 6

От CollectionUtils:

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

Вот полные источники этого метода утилиты:

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

Ответ 7

В то же время не забывайте, что все коллекции конечны, а Iterable не имеет promises. Если что-то есть Iterable, вы можете получить Итератор, и это все.

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

будет расширен до:

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

it.hasNext() не требуется когда-либо возвращать false. Таким образом, в общем случае вы не можете ожидать, что сможете конвертировать каждый Iterable в коллекцию. Например, вы можете перебирать все положительные натуральные числа, перебирать что-то с циклами в нем, которые повторяют одни и те же результаты снова и снова и т.д.

В противном случае: ответ Атри довольно неплох.

Ответ 8

Я использую FluentIterable.from(myIterable).toList() много.

Ответ 9

Это не ответ на ваш вопрос, но я считаю, что это решение вашей проблемы. Интерфейс org.springframework.data.repository.CrudRepository действительно имеет методы, возвращающие java.lang.Iterable, но вы не должны использовать этот интерфейс. Вместо этого используйте вспомогательные интерфейсы в вашем случае org.springframework.data.mongodb.repository.MongoRepository. Этот интерфейс имеет методы, возвращающие объекты типа java.util.List.

Ответ 10

Я использую свою специальную утилиту для создания существующей коллекции, если она доступна.

Main:

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

В идеале вышеуказанное использование ImmutableList, но ImmutableCollection не допускает нулей, которые могут давать нежелательные результаты.

Тесты:

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

Я реализую аналогичные утилиты для всех подтипов коллекций (Set, List и т.д.). Я думаю, что они уже будут частью Гуавы, но я ее не нашел.

Ответ 11

Как только вы вызываете contains, containsAll, equals, hashCode, remove, retainAll, size или toArray, вам все равно придется пересекать элементы.

Если вы иногда вызываете только методы, такие как isEmpty или clear, я полагаю, вам будет лучше, создав коллекцию лениво. Например, у вас есть поддержка ArrayList для хранения ранее повторяющихся элементов.

Я не знаю какого-либо такого класса в любой библиотеке, но это должно быть довольно простое упражнение для записи.

Ответ 12

В Java 8 вы можете сделать это, чтобы добавить все элементы из Iterable в Collection и вернуть его:

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

Вдохновленный ответом @Afreys.

Ответ 13

Так как RxJava - это молот, и этот вид выглядит как гвоздь, вы можете сделать

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

Ответ 14

Здесь SSCCE для отличного способа сделать это в 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);
    }
}

Ответ 15

Два замечания

  • Нет необходимости конвертировать Iterable в коллекцию для использования foreach loop - Iterable может использоваться в таком цикле напрямую, нет синтаксическая разница, поэтому я почти не понимаю, почему исходный вопрос был задан вообще.
  • Предлагаемый способ преобразования Iterable в Collection небезопасен (то же самое относится к CollectionUtils) - нет гарантии, что последующие вызовы метода next() возвращают разные экземпляры объектов. Более того, эта проблема не является чисто теоретической. Например. Итеративная реализация, используемая для передачи значений методу уменьшения Hadoop Reducer, всегда возвращает один и тот же экземпляр значения, только с разными значениями поля. Поэтому, если вы примените makeCollection сверху (или CollectionUtils.addAll(Iterator)), вы получите коллекцию со всеми идентичными элементами.

Ответ 16

Попробуйте StickyList из Cactoos:

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