将迭代器转换为列表

给定Iterator<Element>,我们如何方便地将Iterator转换为List<Element>,以便我们可以对其使用List的操作,如get(index)add(element)等。

315151 次浏览

你可以像这样复制一个迭代器到一个新列表:

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]
List result = new ArrayList();
while (i.hasNext()){
result.add(i.next());
}

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

样本容量为10,000 runs的迭代器的参数为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));

这是假设列表中包含字符串。

最好使用番石榴这样的库:

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

使用谷歌番石榴 !

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

++

你也可以使用Apache commons-collections中的IteratorUtils,尽管它不支持泛型:

List list = IteratorUtils.toList(iterator);

非常简洁的解决方案与普通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)
);
}

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

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

注意在IterableIterator之间有一个区别。

如果你有一个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());

试试Cactoos中的StickyList:

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

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

我只是想指出不会工作的一个看似明显的解决方案:

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

这是因为Stream#generate(Supplier<T>)只能创建无限流,它并不期望其参数抛出NoSuchElementException(这是Iterator#next()最终会做的事情)。

如果你选择迭代器→流→列表的方式,则应该使用xehpuk的答案

没有外部依赖,这是一个使用Streams和java 16 toList()的一行程序。

给定Iterator<?> iterator:

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