282

Given Iterator<Element>, how can we conveniently convert that Iterator to a List<Element>, so that we can use List's operations on it such as get(index), add(element), etc.

Raedwald
  • 46,613
  • 43
  • 151
  • 237
Maksim
  • 16,635
  • 27
  • 94
  • 135

12 Answers12

399

Better use a library like Guava:

import com.google.common.collect.Lists;

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

Another Guava example:

ImmutableList.copyOf(myIterator);

or Apache Commons Collections:

import org.apache.commons.collections.IteratorUtils;

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

List<Element> myList = IteratorUtils.toList(myIterator);       
Erik Hesselink
  • 2,420
  • 21
  • 25
Renaud
  • 16,073
  • 6
  • 81
  • 79
  • 11
    I don't get it. In what way is the ArrayList returned by, say, Guava any better than a normal ArrayList? Do they do it in a more efficient way? Even if it is more efficient is it really worth adding an extra dependency (and more complexity) to your project? – CorayThan Feb 24 '13 at 23:16
  • 11
    @CorayThan less code + tested methods. Though I agree with you I would not add an extra dependency just to use that method. But then again, most of my (large) projects use either Guava or Apache Commons... – Renaud Feb 25 '13 at 22:31
  • 4
    @CorayThan Divide and conquer my friend. Why write a method that is already provided by a library and is tested? We are using a lot of Apache Commons and Guava, they are just awesome and help you save time and money. – Stephan Nov 03 '15 at 14:20
  • 5
    Agree with Renaud and Stephan. Also, if you're using a build tool it's the easiest thing in the world to include these libraries... ALSO... if you really don't want to include them you can go to the source of Apache/Guava code and copy what they've done there: FAR better than wasting your time reinventing the hundreds of beautifully engineered and tested wheels which are already out there. – mike rodent Mar 02 '18 at 09:50
280

In Java 8, you can use the new forEachRemaining method that's been added to the Iterator interface:

List<Element> list = new ArrayList<>();
iterator.forEachRemaining(list::add);
Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
  • 2
    what means `::`? what name has it? seems a direct reference to list.add(); and seems also some java8 new thing; and thanks! :) – Aquarius Power Mar 02 '15 at 22:15
  • 16
    @AquariusPower The `::` syntax is new in Java 8, and it refers to a "method reference," which is a shorthand form of a lambda. See here for further info: http://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html – Stuart Marks Mar 03 '15 at 01:27
  • 1
    where is `iterator` coming from? it is an unresolved symbol – WestCoastProjects Apr 23 '18 at 03:34
  • 2
    @javadba The original question was about how to convert an iterator that you already have into a new ArrayList. For purposes of this example, the iterator you already have is assumed to be called `iterator`. – Stuart Marks Apr 23 '18 at 17:46
  • 3
    This should be the accepted answer, as it's the shortest and do not depend on 3rd party libraries – DanielCuadra Jun 26 '18 at 17:40
73

You can copy an iterator to a new list like this:

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

That's assuming that the list contains strings. There really isn't a faster way to recreate a list from an iterator, you're stuck with traversing it by hand and copying each element to a new list of the appropriate type.

EDIT :

Here's a generic method for copying an iterator to a new list in a type-safe way:

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

Use it like this:

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]
Óscar López
  • 232,561
  • 37
  • 312
  • 386
30

Note there is a difference between Iterable and Iterator.

If you have an Iterable, then with Java 8 you can use this solution:

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

As I know Collectors.toList() creates ArrayList instance.

Actually in my opinion, it also looks good in one line.
For example if you need to return List<Element> from some method:

return StreamSupport.stream(iter.spliterator(), false).collect(Collectors.toList());
rustyx
  • 80,671
  • 25
  • 200
  • 267
Dub Nazar
  • 522
  • 6
  • 12
  • 5
    The question is about Iterator as a starting point, not Iterable. – Jaap Jul 17 '17 at 14:47
  • 1
    agree with the above comment. super confusing that you named your `Iterable` `iterator`. They are *completely* different. – Stephen Harrison Jun 02 '18 at 15:03
  • In Java 8 you can easily convert an `Iterator` back to a **single use** `Iterable` by using `() -> iterator`. This can be useful in situations like this, but let me stress the important thing again: You can use this method only if a **single use** `Iterable` is acceptable. Calling `iterable.iterator()` more than once will yield unexpected results. The above answer then becomes `Iterator iterator = createIterator();` `List array = StreamSupport.stream(((Iterable) () -> iterable).spliterator(), false).collect(toList());` – neXus Dec 11 '18 at 13:05
24

You can also use IteratorUtils from Apache commons-collections, although it doesn't support generics:

List list = IteratorUtils.toList(iterator);
zb226
  • 9,586
  • 6
  • 49
  • 79
yegor256
  • 102,010
  • 123
  • 446
  • 597
  • It also has 2 toArray Method and 1 accepts a type :http://commons.apache.org/proper/commons-collections/javadocs/api-3.2.1/org/apache/commons/collections/IteratorUtils.html#toArray(java.util.Iterator, java.lang.Class) – dwana Sep 23 '15 at 07:49
10

Pretty concise solution with plain Java 8 using 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)
    );
}
Magic-Mouse
  • 603
  • 10
  • 21
xehpuk
  • 7,814
  • 3
  • 30
  • 54
  • Is there a more compact way to write this using stream api? It seems not simpler than the normal while loop way. – xi.lin Jan 02 '15 at 13:39
  • 46
    I would not call that solution "concise". – Sergio Feb 16 '15 at 19:28
  • 1
    @Sergio That's why I wrote "pretty". However, it doesn't need local variables and only one semicolon. You may shorten it with static imports. – xehpuk Feb 16 '15 at 21:03
  • 1
    I'd say `iterator.forEachRemaining( list::add )`, also new in Java 8, is a lot more concise. Pushing the list variable into `Collector` does not improve readibility in this case, since it has to be supported by a `Stream` and a `Spliterator`. – Sheepy Mar 11 '15 at 06:14
  • 1
    @Sergio It's not short, but it's a single expression, which makes a huge difference. – michaelsnowden Jun 23 '16 at 06:11
7

Without external dependency, here's a one-liner using Streams and java 16 toList().

Given an Iterator<?> iterator:

List<?> list = StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false).toList();
apflieger
  • 912
  • 10
  • 18
6
List result = new ArrayList();
while (i.hasNext()){
    result.add(i.next());
}
Akvel
  • 924
  • 1
  • 15
  • 32
  • 11
    @LuggiMendoza: Stack Overflow is not meant to be a place where you can just cut and paste and solve your problem. It is meant to be informative. This is a perfectly informative answer and any reasonable person should be able to put it together that the i was an iterator. From the sounds of it, you put almost no effort into trying to understand what was going on. – CaTalyst.X Jul 10 '13 at 20:39
1

Try StickyList from Cactoos:

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

Disclaimer: I'm one of the developers.

Stephan
  • 41,764
  • 65
  • 238
  • 329
yegor256
  • 102,010
  • 123
  • 446
  • 597
1

I just want to point out a seemingly obvious solution that will NOT work:

List list = Stream.generate(iterator::next)
    .collect(Collectors.toList());

That's because Stream#generate(Supplier<T>) can create only infinite streams, it doesn't expect its argument to throw NoSuchElementException (that's what Iterator#next() will do in the end).

The xehpuk's answer should be used instead if the Iterator→Stream→List way is your choice.

Sasha
  • 3,599
  • 1
  • 31
  • 52
0

use google guava !

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

++

fedevo
  • 631
  • 1
  • 7
  • 15
-3

Here in this case if you want the fastest way possible then for loop is better.

The iterator over a sample size of 10,000 runs takes 40 ms where as for loop takes 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));  

That's assuming that the list contains strings.

vikiiii
  • 9,246
  • 9
  • 49
  • 68
  • 3
    Surely using a `for` loop and accessing a list's elements with `get(i)` is faster than using an iterator... but that's not what the OP was asking, he specifically mentioned that an _iterator_ is given as input. – Óscar López Apr 12 '12 at 03:51
  • @Oscar i am sorry.Should i delete my answer? – vikiiii Apr 12 '12 at 03:52
  • That's your call. I wouldn't delete it yet, it might be informative. I only delete my answers when people start to downvote them :) – Óscar López Apr 12 '12 at 03:54
  • I think @ÓscarLópez is right ... this may not be the required answer, but it contains useful information for readers (like myself :P). – Chthonic Project Aug 05 '13 at 21:09
  • 3
    You have a bug in your answer. In the `get(int)` loop you are only looking at 100k of the 1m entries. If you change that loop to be `it.size()` you will see that these 2 methods are close to the same speed. In general these sorts of java speed tests provide limited real life performance information and should be looked at skeptically. – Gray Mar 12 '18 at 14:28