Перетворити ітератор в 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);       

8
Я не розумію. Яким способом повертається ArrayList, скажімо, Guava, краще, ніж звичайний ArrayList? Чи роблять вони це більш ефективно? Навіть якщо це більш ефективно, чи дійсно варто додати додаткову залежність (і більшу складність) до свого проекту?
CorayThan

10
@CorayThan менше коду + перевірені методи. Хоча я згоден з вами, я б не додав зайвої залежності лише для використання цього методу. Але знову ж таки, більшість моїх (великих) проектів використовують або Guava, або Apache Commons ...
Рено

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походить? це невирішений символ
javadba

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

Чи є більш компактний спосіб написати це за допомогою потоку api? Здається, це не простіше звичайного способу циклу.
xi.lin

37
Я б не назвав це рішення "стислим".
Серхіо

1
@Sergio Тому я написав "досить". Однак йому не потрібні локальні змінні та лише одна крапка з комою. Ви можете скоротити його за допомогою статичного імпорту.
xehpuk

1
Я б сказав iterator.forEachRemaining( list::add ), також новий у Java 8 набагато більш стислий. Натискання на змінну списку Collectorв цьому випадку не покращує читаність, оскільки вона повинна підтримуватися а Streamта a Spliterator.
Шепі

1
@Sergio Це не коротко, але це єдиний вираз, який робить величезну зміну.
michaelsnowden

5
List result = new ArrayList();
while (i.hasNext()){
    result.add(i.next());
}

11
@LuggiMendoza: Переповнення стека не призначене для того, щоб просто вирізати та вставити та вирішити свою проблему. Це покликане бути інформативним. Це ідеально інформативна відповідь, і будь-яка розумна людина повинна бути в змозі скласти це, щоб я був ітератором. Зі звуків цього ви майже не докладаєте зусиль, щоб спробувати зрозуміти, що відбувається.
CaTalyst.X

2

Спробуйте StickyListвід кактусів :

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

Відмова: Я один із розробників.


Це не відповідає на запитання. Iterable! =Iterator
xehpuk

Чудово, тепер вам, мабуть, потрібно сформувати JavaDoc для нової версії та оновити посилання. :)
xehpuk

2

Ось однолінійний за допомогою потоків

Iterator<?> iterator = ...
List<?> list = StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false)
                .collect(Collectors.toList());

1

Я просто хочу зазначити, здавалося б, очевидне рішення, яке НЕ буде працювати:

Список списків = Stream.generate (ітератор :: наступний)
    .collect (Collectors.toList ());

Це тому, що Stream#generate(Supplier<T>)може створювати лише нескінченні потоки, він не очікує, що його аргумент буде викинутий NoSuchElementException(ось що Iterator#next()буде зроблено в результаті).

Натомість слід використовувати відповідь xehpuk, якщо ваш вибір є ітератором → Потік → Список.


0

використовуйте google guava !

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

++


Ітератор (не Ітерабельний) для 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));  

Це припускаючи, що список містить рядки.


3
Безумовно, використання forциклу та отримання доступу до елементів списку get(i)швидше, ніж використання ітератора ... але це не те, про що запитував ОП, він спеціально зазначив, що ітератор надається як вхідний.
Оскар Лопес

@Oscar мені шкода. Я повинен видалити свою відповідь?
vikiiii

Це ваш дзвінок. Я би його ще не видаляв, це може бути інформативно. Я видаляю свої відповіді лише тоді, коли люди починають зволікати з ними :)
Óscar López

Я думаю, що @ ÓscarLópez вірно ... це може бути не потрібною відповіддю, але вона містить корисну інформацію для читачів (як я: P).
Chthonic Project

У вас є помилка у вашій відповіді. У get(int)циклі ви дивитесь лише на 100 к 1m записів. Якщо ви зміните цю петлю, it.size()ви побачите, що ці два методи близькі до однакової швидкості. Загалом, такі випробування на швидкість на Java забезпечують обмежену реальну інформацію про продуктивність, і їх слід розглядати скептично.
Сірий
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.