将Iterator转换为ArrayList


241

考虑到Iterator<Element>,我们怎么可以转换IteratorArrayList<Element>(或List<Element>)的最佳和最快的方式可能的,这样我们就可以用ArrayList的就可以了,如操作get(index)add(element)等等。

Answers:


359

最好使用像Guava这样的库:

import com.google.common.collect.Lists;

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

另一个番石榴的例子:

ImmutableList.copyOf(myIterator);

Apache Commons Collections

import org.apache.commons.collections.IteratorUtils;

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

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

8
我不明白 番石榴返回的ArrayList比普通的ArrayList有什么更好的表现?他们是否以更有效的方式做到这一点?即使效率更高,是否真的值得在项目中增加额外的依赖关系(以及更多的复杂性)?
CorayThan

10
@Coray比更少的代码+经过测试的方法。尽管我同意您的看法,但我不会仅使用该方法就添加额外的依赖项。但是话又说回来,我的大部分(大型)项目都使用Guava或Apache Commons ...
Renaud

4
@CorayThan分开征服我的朋友。为什么编写一个库已经提供并经过测试的方法?我们使用了许多Apache Commons和Guava,它们很棒,可以帮助您节省时间和金钱。
斯蒂芬

5
同意Renaud和Stephan。另外,如果您使用的是构建工具,那么包含这些库是世界上最简单的事情……另外……如果您真的不想包含它们,则可以转到Apache / Guava代码的源代码,然后复制他们在那里所做的工作:FAR比浪费时间重塑已有的数百个经过精心设计和测试的车轮要好。
麦克啮齿动物,

238

在Java 8中,您可以使用forEachRemaining已添加到Iterator接口的新方法:

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

2
什么意思::?它叫什么名字?似乎是对list.add()的直接引用;并且似乎还有一些java8新事物;谢谢!:)
水瓶座力量

13
@AquariusPower ::语法是Java 8中的新增功能,它引用的是“方法引用”,它是lambda的简写形式。看到这里的进一步信息:docs.oracle.com/javase/tutorial/java/javaOO/...
斯图尔特商标

哪里iterator来的?它是一个无法解析的符号
javadba '18

1
@javadba最初的问题是关于如何将已经具有的迭代器转换为新的ArrayList的。就本示例而言,假设您已经拥有的迭代器称为iterator
Stuart Marks

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>。
Jaap

1
同意以上评论。您将自己的名字命名为超级困惑Iterable iterator。他们是完全不同的。
Stephen Harrison

在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编写此方法的更紧凑的方法?似乎不比普通的while循环方法简单。
xi.lin 2015年

37
我不会将该解决方案称为“简洁”。
塞尔吉奥2015年

1
@Sergio这就是为什么我写“漂亮”的原因。但是,它不需要局部变量,而只需要一个分号。您可以使用静态导入来缩短它。
xehpuk 2015年

1
我想说iterator.forEachRemaining( list::add ),Java 8中的新功能也更加简洁。Collector在这种情况下,将list变量压入并不会提高可读性,因为必须由a Stream和a 支持Spliterator
羊皮的话,2015年

1
@Sergio它不短,但是它是一个表达式,因此有很大的不同。
michaelsnowden '16

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

11
@LuggiMendoza:堆栈溢出并不意味着您可以剪切粘贴并解决问题。它旨在提供信息。这是一个非常有用的答案,任何有理智的人都可以将i视为迭代器。从它的声音来看,您几乎没有花力气去尝试了解正在发生的事情。
CaTalyst.X

2

StickyListCactoos尝试:

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

免责声明:我是开发人员之一。


这不能回答问题。Iterable!=Iterator
xehpuk

太好了,现在您可能需要为新版本生成JavaDoc并更新链接。:)
xehpuk

2

这是使用Streams的单线

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



-2

在这种情况下,如果您想要最快的方法,那就for loop更好了。

样本数量为10,000 runstake 的迭代器,40 ms其中for循环需要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)比使用迭代器要快...但这不是OP的要求,他特别提到将迭代器作为输入。
奥斯卡·洛佩斯

@Oscar我很抱歉。我应该删除答案吗?
vikiiii 2012年

那是你的电话。我还不会删除它,可能会提供更多信息。我只是删除我的答案,当人们开始downvote他们:)
奥斯卡·洛佩斯

我认为@ÓscarLópez是正确的……这可能不是必需的答案,但是它包含了一些对读者有用的信息(如我自己:P)。
Chthonic项目

您的答案有错误。在get(int)循环中,您仅查看100万个1m条目。如果您将该循环更改为it.size()您将看到这两种方法接近相同的速度。通常,这类Java速度测试只能提供有限的实际性能信息,应对此持怀疑态度。
灰色
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.