【发布时间】:2012-04-24 09:54:00
【问题描述】:
给定Iterator<Element>,我们如何方便地将Iterator转换为List<Element>,以便我们可以在其上使用List的操作,例如get(index)、add(element)等。
【问题讨论】:
给定Iterator<Element>,我们如何方便地将Iterator转换为List<Element>,以便我们可以在其上使用List的操作,例如get(index)、add(element)等。
【问题讨论】:
最好使用像Guava这样的库:
import com.google.common.collect.Lists;
Iterator<Element> myIterator = ... //some iterator
List<Element> myList = Lists.newArrayList(myIterator);
另一个番石榴示例:
ImmutableList.copyOf(myIterator);
import org.apache.commons.collections.IteratorUtils;
Iterator<Element> myIterator = ...//some iterator
List<Element> myList = IteratorUtils.toList(myIterator);
【讨论】:
在 Java 8 中,您可以使用已添加到 Iterator 接口的新 forEachRemaining 方法:
List<Element> list = new ArrayList<>();
iterator.forEachRemaining(list::add);
【讨论】:
:: 是什么意思?它有什么名字?似乎是对 list.add() 的直接引用;并且似乎还有一些 java8 新事物;谢谢! :)
:: 语法是 Java 8 中的新语法,它指的是“方法引用”,它是 lambda 的简写形式。请参阅此处了解更多信息:docs.oracle.com/javase/tutorial/java/javaOO/…
iterator 来自哪里?这是一个未解析的符号
iterator。
您可以像这样将迭代器复制到新列表中:
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]
【讨论】:
如果您有 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());
【讨论】:
Iterableiterator。它们完全不同。
() -> iterator 轻松地将 Iterator 转换回一次性 Iterable。这在这种情况下可能很有用,但让我再次强调重要的一点:只有在单次使用 Iterable 可以接受的情况下,您才能使用此方法。多次调用iterable.iterator() 会产生意想不到的结果。然后上面的答案变成Iterator<Element> iterator = createIterator();List<Element> array = StreamSupport.stream(((Iterable<Element>) () -> iterable).spliterator(), false).collect(toList());
您也可以使用来自 Apache commons-collections 的 IteratorUtils,尽管它不支持泛型:
List list = IteratorUtils.toList(iterator);
【讨论】:
使用 java.util.stream 的纯 Java 8 非常简洁的解决方案:
public static <T> ArrayList<T> toArrayList(final Iterator<T> iterator) {
return StreamSupport
.stream(
Spliterators
.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false)
.collect(
Collectors.toCollection(ArrayList::new)
);
}
【讨论】:
iterator.forEachRemaining( list::add ) 也是Java 8 中的新功能,它更加简洁。在这种情况下,将列表变量推入Collector 并不能提高可读性,因为它必须得到Stream 和Spliterator 的支持。
没有外部依赖,这是一个使用 Streams 和 java 16 toList() 的单线器。
给定一个Iterator<?> iterator:
List<?> list = StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false).toList();
【讨论】:
List result = new ArrayList();
while (i.hasNext()){
result.add(i.next());
}
【讨论】:
我只想指出一个看似显而易见但行不通的解决方案:
那是因为Stream#generate(Supplier<T>) 只能创建无限流,它不希望它的参数抛出NoSuchElementException(这就是Iterator#next() 最终会做的事情)。
如果您选择迭代器→流→列表方式,则应使用The xehpuk's answer。
【讨论】:
【讨论】:
Iterable != Iterator
使用谷歌guava!
Iterable<String> fieldsIterable = ...
List<String> fields = Lists.newArrayList(fieldsIterable);
++
【讨论】:
在这种情况下,如果您想要尽可能快的方式,那么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));
假设列表包含字符串。
【讨论】:
for 循环并使用get(i) 访问列表的元素比使用迭代器要快......但这不是OP所要求的,他特别提到了一个 迭代器作为输入给出。
get(int) 循环中,您只查看 1m 条目中的 100k。如果您将该循环更改为it.size(),您将看到这两种方法的速度接近相同。一般来说,这些类型的 java 速度测试提供有限的现实生活中的性能信息,应该持怀疑态度。