Konwertuj Iterator na ArrayList


241

Biorąc pod uwagę Iterator<Element>, w jaki sposób możemy przekonwertować Iteratordo ArrayList<Element>(lub List<Element>) w najlepszym i najszybszym sposobem możliwe, tak, że możemy użyć ArrayList„s operacje na takich jak on get(index), add(element)itp

Odpowiedzi:


360

Lepiej użyj biblioteki takiej jak Guava :

import com.google.common.collect.Lists;

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

Kolejny przykład guawy:

ImmutableList.copyOf(myIterator);

lub Kolekcje Apache Commons :

import org.apache.commons.collections.IteratorUtils;

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

List<Element> myList = IteratorUtils.toList(myIterator);       

8
Nie rozumiem W jaki sposób ArrayList jest zwracany przez, powiedzmy, Guava lepiej niż zwykły ArrayList? Czy robią to w bardziej wydajny sposób? Nawet jeśli jest bardziej wydajny, czy naprawdę warto dodać do projektu dodatkową zależność (i większą złożoność)?
— CorayThan

10
@CorayThan mniej kodu + przetestowane metody. Chociaż zgadzam się z tobą, nie dodałbym dodatkowej zależności tylko po to, aby użyć tej metody. Ale z drugiej strony większość moich (dużych) projektów korzysta z Guava lub Apache Commons ...
— Renaud

4
@CorayThan Podziel i podbij mojego przyjaciela. Po co pisać metodę, która jest już udostępniana przez bibliotekę i jest testowana? Używamy dużo Apache Commons i Guava, są po prostu niesamowite i pomagają zaoszczędzić czas i pieniądze.
— Stephan

5
Zgadzam się z Renaudem i Stephanem. Ponadto, jeśli używasz narzędzia do kompilacji, najłatwiej jest na świecie włączyć te biblioteki ... TAKŻE ... jeśli naprawdę nie chcesz ich uwzględniać, możesz przejść do źródła kodu Apache / Guava i skopiuj to, co tam zrobili: DALEJ lepiej niż marnować swój czas na nowo opracowując setki pięknie zaprojektowanych i przetestowanych kół, które już tam są.
— Mike gryzoń

238

W Javie 8 możesz użyć nowej forEachRemainingmetody dodanej do Iteratorinterfejsu:

List<Element> list = new ArrayList<>();
iterator.forEachRemaining(list::add);

2
co to znaczy ::? jakie to ma imię wydaje się bezpośrednim odniesieniem do list.add (); i wydaje się też, że jest coś nowego w java8; i dzięki! :)
— Aquarius Power

13
@AquariusPower ::Składnia jest nowa w Javie 8 i odnosi się do „odwołania do metody”, które jest skrótową formą lambda. Zobacz tutaj, aby uzyskać więcej informacji: docs.oracle.com/javase/tutorial/java/javaOO/…
— Stuart Marks

skąd iteratorpochodzi? jest to nierozwiązany symbol
— javadba,

1
@javadba Pierwotne pytanie dotyczyło sposobu konwersji iteratora, który już masz, w nową ArrayList. Na potrzeby tego przykładu zakłada się, że iterator, który już masz, został wywołany iterator.
— Stuart Marks

1
To powinna być zaakceptowana odpowiedź, ponieważ jest najkrótsza i nie zależy od bibliotek stron trzecich
— DanielCuadra

64

Możesz skopiować iterator do nowej listy, takiej jak ta:

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

Zakłada się, że lista zawiera ciągi znaków. Naprawdę nie ma szybszego sposobu na odtworzenie listy z iteratora, utkniesz w ręcznym przechodzeniu do niej i kopiowaniu każdego elementu na nową listę odpowiedniego typu.

EDYTOWAĆ :

Oto ogólna metoda kopiowania iteratora na nową listę w sposób bezpieczny dla typu:

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

Użyj tego w ten sposób:

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

Uwaga: istnieje różnica między Iterablei Iterator.

Jeśli masz Iterable, to w Javie 8 możesz użyć tego rozwiązania:

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

Jak wiem Collectors.toList()tworzy ArrayListinstancję.

Moim zdaniem w jednym wierszu wygląda dobrze.
Na przykład, jeśli chcesz wrócić List<Element>z jakiejś metody:

return StreamSupport.stream(iter.spliterator(), false).collect(Collectors.toList());

4
Pytanie dotyczy Iteratora <Element> jako punktu wyjścia, a nie Iterowalnego <Element>.
— Jaap

1
zgadzam się z powyższym komentarzem. bardzo mylące, że nazwałeś swój Iterable iterator. Oni są zupełnie inni.
— Stephen Harrison

W Javie 8 możesz łatwo przekonwertować Iteratorpowrót do pojedynczego użycia Iterable za pomocą () -> iterator. Może to być przydatne w takich sytuacjach, ale pozwólcie, że jeszcze raz podkreślę ważną rzecz: Możesz użyć tej metody tylko wtedy, gdy jedno użycie Iterable jest dopuszczalne. Dzwonienie iterable.iterator()więcej niż raz da nieoczekiwane rezultaty. Powyższa odpowiedź staje sięIterator<Element> iterator = createIterator(); List<Element> array = StreamSupport.stream(((Iterable<Element>) () -> iterable).spliterator(), false).collect(toList());
— Nexus


9

Całkiem zwięzłe rozwiązanie z prostym Java 8 za pomocą 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)
    );
}

Czy istnieje bardziej kompaktowy sposób na napisanie tego przy użyciu interfejsu API Stream? Wydaje się, że nie jest to prostsze niż normalny sposób pętli while.
— xi.lin

37
Nie nazwałbym tego rozwiązania „zwięzłym”.
— Sergio

1
@Sergio Właśnie dlatego napisałem „ładny”. Jednak nie potrzebuje zmiennych lokalnych i tylko jednego średnika. Możesz go skrócić za pomocą importu statycznego.
— xehpuk

1
Powiedziałbym, że iterator.forEachRemaining( list::add )także nowy w Javie 8 jest o wiele bardziej zwięzły. CollectorWepchnięcie zmiennej listy do nie poprawia czytelności w tym przypadku, ponieważ musi być obsługiwana przez a Streami a Spliterator.
— Sheepy

1
@Sergio Nie jest krótki, ale to pojedyncze wyrażenie, które robi ogromną różnicę.
— michaelsnowden

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

11
@LuggiMendoza: Przepełnienie stosu nie jest miejscem, w którym można po prostu wyciąć, wkleić i rozwiązać problem. Ma to być informacyjny. To jest doskonale pouczająca odpowiedź i każda rozsądna osoba powinna być w stanie złożyć w całość, że ja byłam iteratorem. Z dźwięków tego prawie nie starasz się zrozumieć, co się dzieje.
— CaTalyst.X

2

Spróbuj StickyListz Cactoos :

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

Oświadczenie: Jestem jednym z programistów.


To nie odpowiada na pytanie. Iterable! =Iterator
— xehpuk

Świetnie, teraz prawdopodobnie musisz wygenerować JavaDoc dla nowej wersji i zaktualizować link. :)
— xehpuk

2

Oto linijka korzystająca ze strumieni

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


0

użyj google guava !

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

++


Iterator (nieodwracalny) do ArrayList
— Chthonic Project

-2

Tutaj w tym przypadku, jeśli chcesz najszybszy możliwy sposób, for loopto lepiej.

Iterator na próbce wielkości 10,000 runsujęć, 40 msgdzie dla ujęć pętli2 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));  

Zakłada się, że lista zawiera ciągi znaków.


3
Z pewnością używanie forpętli i uzyskiwanie dostępu do elementów listy get(i)jest szybsze niż używanie iteratora ... ale nie o to prosił OP, szczególnie wspomniał, że iterator jest podawany jako dane wejściowe.
— Óscar López

@Oscar przepraszam. Czy powinienem usunąć swoją odpowiedź?
— vikiiii

To twój telefon. Nie usunęłbym tego jeszcze, może to być pouczające. Usuwam odpowiedzi tylko wtedy, gdy ludzie zaczynają je głosować :)
— Óscar López

Myślę, że @ ÓscarLópez ma rację ... to może nie być wymagana odpowiedź, ale zawiera przydatne informacje dla czytelników (takich jak ja: P).
— Chthonic Project,

Masz błąd w swojej odpowiedzi. W get(int)pętli patrzysz tylko na 100 000 wpisów o długości 1 m. Jeśli zmienisz tę pętlę it.size(), zobaczysz, że te 2 metody są zbliżone do tej samej prędkości. Ogólnie rzecz biorąc, tego rodzaju testy prędkości java dostarczają ograniczonych informacji na temat rzeczywistych wyników i należy na nie spojrzeć sceptycznie.
— Gray
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.