Конвертировать Iterator в ArrayList

241

Учитывая Iterator<Element>, как мы можем преобразовать это Iteratorв ArrayList<Element>(или List<Element>) наилучшим и самым быстрым способом, чтобы мы могли использовать ArrayListтакие операции над ним, как get(index), add(element)и т. Д.

Максим
источник

Ответы:

360

Лучше использовать такую ​​библиотеку, как Guava :

import com.google.common.collect.Lists;

Iterator<Element> myIterator = ... //some iterator
List<Element> myList = Lists.newArrayList(myIterator);

Еще один пример гуавы:

ImmutableList.copyOf(myIterator);

или коллекции Apache Commons :

import org.apache.commons.collections.IteratorUtils;

Iterator<Element> myIterator = ...//some iterator

List<Element> myList = IteratorUtils.toList(myIterator);       
Renaud
источник
8
Я не понимаю Каким образом ArrayList возвращается, скажем, Guava лучше, чем обычный ArrayList? Они делают это более эффективным способом? Даже если он более эффективен, стоит ли добавлять дополнительную зависимость (и более сложную) в ваш проект?
CorayThan
10
@CorayThan меньше кода + проверенные методы. Хотя я согласен с вами, я бы не стал добавлять дополнительную зависимость только для использования этого метода. Но опять же, большинство моих (больших) проектов используют либо Guava, либо Apache Commons ...
Renaud
4
@CorayThan Разделяй и властвуй, мой друг. Зачем писать метод, который уже предоставлен библиотекой и протестирован? Мы используем много Apache Commons и Guava, они просто потрясающие и помогают вам сэкономить время и деньги.
Стефан
5
Согласна с Рено и Стефаном. Кроме того, если вы используете инструмент сборки, проще всего в мире включить эти библиотеки ... ТАКЖЕ ... если вы действительно не хотите их включать, вы можете перейти к исходному коду Apache / Guava и скопируйте то, что они сделали там: НАМНОГО лучше, чем тратить свое время на переизобретение сотен прекрасно сконструированных и испытанных колес, которые уже существуют.
Майк Грызун
238

В Java 8 вы можете использовать новый forEachRemainingметод, который был добавлен в Iteratorинтерфейс:

List<Element> list = new ArrayList<>();
iterator.forEachRemaining(list::add);
Стюарт Маркс
источник
2
что значит ::? какое имя у него есть? кажется прямой ссылкой на list.add (); и кажется также некоторая новая вещь java8; и спасибо! :)
Водолей Власть
13
@AquariusPower ::Синтаксис является новым в Java 8, и он ссылается на «ссылку на метод», которая является сокращенной формой лямбды. Смотрите здесь для получения дополнительной информации: docs.oracle.com/javase/tutorial/java/javaOO/…
Стюарт Маркс
откуда iteratorидет? это неразрешенный символ
Джавадба
1
@javadba Первоначальный вопрос был о том, как преобразовать имеющийся у вас итератор в новый ArrayList. Для целей этого примера предполагается, что уже имеющийся у вас итератор вызван iterator.
Стюарт Маркс
1
Это должен быть принятый ответ, так как он самый короткий и не зависит от сторонних библиотек
DanielCuadra
64

Вы можете скопировать итератор в новый список, например так:

Iterator<String> iter = list.iterator();
List<String> copy = new ArrayList<String>();
while (iter.hasNext())
    copy.add(iter.next());

Это при условии, что список содержит строки. На самом деле не существует более быстрого способа воссоздания списка из итератора, вы застряли на том, чтобы обходить его вручную и копировать каждый элемент в новый список соответствующего типа.

РЕДАКТИРОВАТЬ :

Вот общий метод для копирования итератора в новый список безопасным для типов способом:

public static <T> List<T> copyIterator(Iterator<T> iter) {
    List<T> copy = new ArrayList<T>();
    while (iter.hasNext())
        copy.add(iter.next());
    return copy;
}

Используйте это так:

List<String> list = Arrays.asList("1", "2", "3");
Iterator<String> iter = list.iterator();
List<String> copy = copyIterator(iter);
System.out.println(copy);
> [1, 2, 3]
Оскар Лопес
источник
26

Обратите внимание, что есть разница между Iterableи Iterator.

Если у вас есть Iterable, то с Java 8 вы можете использовать это решение:

Iterable<Element> iterable = createIterable();
List<Element> array = StreamSupport
    .stream(iterable.spliterator(), false)
    .collect(Collectors.toList());

Как я знаю, Collectors.toList()создает ArrayListэкземпляр.

На самом деле, на мой взгляд, это также хорошо выглядит в одной строке.
Например, если вам нужно вернуться List<Element>из какого-либо метода:

return StreamSupport.stream(iter.spliterator(), false).collect(Collectors.toList());
Дуб назар
источник
4
Вопрос в том, что Iterator <Element> является отправной точкой, а не Iterable <Element>.
Яап
1
согласен с приведенным выше комментарием. очень запутанно, что ты назвал свой Iterable iterator. Они совершенно разные.
Стивен Харрисон
В Java 8 вы можете легко конвертировать Iteratorобратно в одно использование Iterable с помощью () -> iterator. Это может быть полезно в подобных ситуациях, но позвольте мне еще раз подчеркнуть важную вещь: вы можете использовать этот метод, только если допустимо одноразовое использование Iterable . Звонок iterable.iterator()более одного раза даст неожиданные результаты. Выше ответ становитсяIterator<Element> iterator = createIterator(); List<Element> array = StreamSupport.stream(((Iterable<Element>) () -> iterable).spliterator(), false).collect(toList());
NEXUS
9

Довольно краткое решение с простой Java 8, используя java.util.stream:

public static <T> ArrayList<T> toArrayList(final Iterator<T> iterator) {
    return StreamSupport
        .stream(
            Spliterators
                .spliteratorUnknownSize(iterator, Spliterator.ORDERED), false)
        .collect(
                Collectors.toCollection(ArrayList::new)
    );
}
xehpuk
источник
Есть ли более компактный способ написать это с помощью потокового API? Кажется, не проще, чем обычный цикл while.
xi.lin
37
Я бы не назвал это решение "кратким".
Серхио
1
@ Sergio Вот почему я написал "довольно". Однако для этого не нужны локальные переменные и только одна точка с запятой. Вы можете сократить его с помощью статического импорта.
xehpuk
1
Я бы сказал iterator.forEachRemaining( list::add ), также новый в Java 8, гораздо более кратким. CollectorВставка переменной списка в не улучшает читаемость в этом случае, так как она должна поддерживаться a Streamи a Spliterator.
Sheepy
1
@ Sergio Это не коротко, но это одно выражение, которое имеет огромное значение.
michaelsnowden
5
List result = new ArrayList();
while (i.hasNext()){
    result.add(i.next());
}
Akvel
источник
11
@LuggiMendoza: Переполнение стека не предназначено для того, чтобы вы могли просто вырезать, вставлять и решать свою проблему. Это должно быть информативно. Это совершенно информативный ответ, и любой разумный человек должен иметь возможность собрать его, чтобы я был итератором. Судя по звукам, вы почти не прикладываете усилий, пытаясь понять, что происходит.
CaTalyst.X 10.07.13
2

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

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

Отказ от ответственности: я один из разработчиков.

yegor256
источник
Это не отвечает на вопрос. Iterable! =Iterator
xehpuk
Отлично, теперь вам, вероятно, нужно сгенерировать JavaDoc для новой версии и обновить ссылку. :)
xehpuk
2

Вот одна строка, использующая потоки

Iterator<?> iterator = ...
List<?> list = StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false)
                .collect(Collectors.toList());
apflieger
источник
1

Я просто хочу указать на кажущееся очевидным решение, которое НЕ будет работать:

Список список = Stream.generate (итератор :: следующий)
    .collect (Collectors.toList ());

Это потому, что Stream#generate(Supplier<T>)может создавать только бесконечные потоки, он не ожидает, что его аргумент будет выдан NoSuchElementException(это то, что Iterator#next()будет делать в конце).

Вместо этого следует использовать ответ xehpuk, если вы выбрали путь Iterator → Stream → List.

Саша
источник
0

использовать гугл гуава !

Iterable<String> fieldsIterable = ...
List<String> fields = Lists.newArrayList(fieldsIterable);

++

fedevo
источник
Итератор (не повторяемый) для ArrayList
Chthonic Project
-2

Вот в этом случае, если вы хотите самый быстрый путь, то for loopлучше.

Итератор над образцом размером 10,000 runsпринимает , 40 msгде за цикл занимает2 ms

        ArrayList<String> alist = new ArrayList<String>();  
        long start, end;  

        for (int i = 0; i < 1000000; i++) {  
            alist.add(String.valueOf(i));  
        }  

        ListIterator<String> it = alist.listIterator();      

        start = System.currentTimeMillis();  
        while (it.hasNext()) {  
            String s = it.next();  
        }  
        end = System.currentTimeMillis();  

        System.out.println("Iterator start: " + start + ", end: " + end + ", delta: "  
            + (end - start));  
        start = System.currentTimeMillis();  
        int ixx = 0;  
        for (int i = 0; i < 100000; i++) {  
            String s = alist.get(i);  
        }  

        System.out.println(ixx);  
        end = System.currentTimeMillis();  
        System.out.println("for loop start: " + start + ", end: " + end + ", delta: "  
            + (end - start));  

Это при условии, что список содержит строки.

vikiiii
источник
3
Конечно, использование forцикла и доступ к элементам списка get(i)быстрее, чем использование итератора ... но это не то, о чем спрашивал ОП, он специально упомянул, что в качестве входных данных указан итератор .
Оскар Лопес
@ Оскар, прости. Должен ли я удалить свой ответ?
vikiiii
Это твой звонок. Я бы еще не удалил, это может быть информативно. Я удаляю свои ответы только тогда, когда люди начинают их понижать :)
Оскар Лопес
Я думаю, что @ ÓscarLópez прав ... это, возможно, не требуемый ответ, но он содержит полезную информацию для читателей (таких как я: P).
Хтонический проект
У вас есть ошибка в вашем ответе. В get(int)цикле вы просматриваете только 100 тыс. Записей из 1 млн. Если вы измените этот цикл, it.size()вы увидите, что эти 2 метода близки к одинаковой скорости. В целом, такие тесты скорости Java предоставляют ограниченную информацию о производительности в реальной жизни и должны рассматриваться скептически.
Серый