【问题标题】:Convert Iterator to List将迭代器转换为列表
【发布时间】:2012-04-24 09:54:00
【问题描述】:

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

【问题讨论】:

    标签: java list iterator


    【解决方案1】:

    最好使用像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);       
    

    【讨论】:

    • 我不明白。比如说,Guava 返回的 ArrayList 比普通的 ArrayList 好多少?他们是否以更有效的方式做到这一点?即使它更有效,是否真的值得为您的项目添加额外的依赖项(和更复杂的)?
    • @CorayThan 更少的代码 + 测试的方法。虽然我同意你的观点,但我不会为了使用该方法而添加额外的依赖项。但是话又说回来,我的大多数(大型)项目都使用 Guava 或 Apache Commons...
    • @CorayThan 分而治之,征服我的朋友。为什么要编写一个库已经提供并经过测试的方法?我们使用了很多 Apache Commons 和 Guava,它们非常棒,可以帮助您节省时间和金钱。
    • 同意 Renaud 和 Stephan 的观点。此外,如果您使用的是构建工具,那么包含这些库是世界上最简单的事情......另外......如果您真的不想包含它们,您可以转到 Apache/Guava 代码的源代码和复制他们在那里所做的:比浪费时间重新发明已经存在的数百个经过精心设计和测试的车轮要好得多。
    【解决方案2】:

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

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

    【讨论】:

    • :: 是什么意思?它有什么名字?似乎是对 list.add() 的直接引用;并且似乎还有一些 java8 新事物;谢谢! :)
    • @AquariusPower :: 语法是 Java 8 中的新语法,它指的是“方法引用”,它是 lambda 的简写形式。请参阅此处了解更多信息:docs.oracle.com/javase/tutorial/java/javaOO/…
    • iterator 来自哪里?这是一个未解析的符号
    • @javadba 最初的问题是关于如何将已有的迭代器转换为新的 ArrayList。出于本示例的目的,假设您已经拥有的迭代器名为 iterator
    • 这应该是公认的答案,因为它是最短的并且不依赖于 3rd 方库
    【解决方案3】:

    您可以像这样将迭代器复制到新列表中:

    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]
    

    【讨论】:

      【解决方案4】:

      注意IterableIterator 之间存在差异。

      如果您有 Iterable,那么对于 Java 8,您可以使用以下解决方案:

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

      据我所知Collectors.toList() 创建ArrayList 实例。

      其实在我看来,一行也好看。
      例如,如果您需要从某个方法返回List&lt;Element&gt;

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

      【讨论】:

      • 问题是以 Iterator 为起点,而不是 Iterable
      • 同意上述评论。超级困惑,你命名你的Iterableiterator。它们完全不同。
      • 在 Java 8 中,您可以使用 () -&gt; iterator 轻松地将 Iterator 转换回一次性 Iterable。这在这种情况下可能很有用,但让我再次强调重要的一点:只有在单次使用 Iterable 可以接受的情况下,您才能使用此方法。多次调用iterable.iterator() 会产生意想不到的结果。然后上面的答案变成Iterator&lt;Element&gt; iterator = createIterator();List&lt;Element&gt; array = StreamSupport.stream(((Iterable&lt;Element&gt;) () -&gt; iterable).spliterator(), false).collect(toList());
      【解决方案5】:

      您也可以使用来自 Apache commons-collectionsIteratorUtils,尽管它不支持泛型:

      List list = IteratorUtils.toList(iterator);
      

      【讨论】:

      【解决方案6】:

      使用 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)
          );
      }
      

      【讨论】:

      • 有没有更紧凑的方式来使用流 api 编写这个?它似乎并不比普通的while循环方式简单。
      • 我不会称该解决方案“简洁”。
      • @Sergio 这就是我写“漂亮”的原因。但是,它不需要局部变量,只需要一个分号。您可以使用静态导入来缩短它。
      • 我想说iterator.forEachRemaining( list::add ) 也是Java 8 中的新功能,它更加简洁。在这种情况下,将列表变量推入Collector 并不能提高可读性,因为它必须得到StreamSpliterator 的支持。
      • @Sergio 不短,但它是一个单一的表达方式,它有很大的不同。
      【解决方案7】:

      没有外部依赖,这是一个使用 Streams 和 java 16 toList() 的单线器。

      给定一个Iterator&lt;?&gt; iterator

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

      【讨论】:

      • 可以将并行设置为 'true' 以使列表更快。
      【解决方案8】:
      List result = new ArrayList();
      while (i.hasNext()){
          result.add(i.next());
      }
      

      【讨论】:

      • @LuggiMendoza:Stack Overflow 并不是一个您可以剪切和粘贴并解决问题的地方。它旨在提供信息。这是一个内容丰富的答案,任何合理的人都应该能够将 i 是一个迭代器放在一起。从它的声音来看,您几乎没有花任何精力去尝试了解发生了什么。
      【解决方案9】:

      我只想指出一个看似显而易见但行不通的解决方案:

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

      那是因为Stream#generate(Supplier&lt;T&gt;) 只能创建无限流,它不希望它的参数抛出NoSuchElementException(这就是Iterator#next() 最终会做的事情)。

      如果您选择迭代器→流→列表方式,则应使用The xehpuk's answer

      【讨论】:

        【解决方案10】:

        Cactoos 尝试StickyList

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

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

        【讨论】:

        • 这不能回答问题。 Iterable != Iterator
        • 太好了,现在您可能需要为新版本生成 JavaDoc 并更新链接。 :)
        【解决方案11】:

        使用谷歌guava

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

        ++

        【讨论】:

        • 迭代器(不可迭代)到 ArrayList
        【解决方案12】:

        在这种情况下,如果您想要尽可能快的方式,那么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所要求的,他特别提到了一个 迭代器作为输入给出。
        • @Oscar 对不起。我应该删除我的答案吗?
        • 这是您的电话。我不会删除它,它可能会提供信息。只有当人们开始反对我的答案时,我才会删除它们:)
        • 我认为@ÓscarLópez 是对的......这可能不是必需的答案,但它包含对读者有用的信息(比如我自己:P)。
        • 您的回答中有错误。在 get(int) 循环中,您只查看 1m 条目中的 100k。如果您将该循环更改为it.size(),您将看到这两种方法的速度接近相同。一般来说,这些类型的 java 速度测试提供有限的现实生活中的性能信息,应该持怀疑态度。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-04
        • 2011-04-16
        • 2011-09-02
        • 2021-07-12
        • 1970-01-01
        • 2020-05-18
        相关资源
        最近更新 更多