イテレータをArrayListに変換


241

与えられIterator<Element>、どのように我々はそれを変換することができますIteratorArrayList<Element>(またはList<Element>中)の最善かつ迅速なので、我々が使用できることを、可能な方法ArrayListのような、その上の操作get(index)add(element)など

回答:


360

Guavaのようなライブラリを使用するほうがよいです。

import com.google.common.collect.Lists;

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

別のグアバの例:

ImmutableList.copyOf(myIterator);

またはApache Commons Collection

import org.apache.commons.collections.IteratorUtils;

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

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

8
わかりません。たとえば、Guavaによって返されるArrayListは、通常のArrayListよりも優れていますか?彼らはそれをより効率的な方法で行っていますか?それがより効率的であっても、プロジェクトに追加の依存関係(およびより複雑なもの)を追加する価値は本当にありますか?
CorayThan 2013

10
@CorayThanより少ないコード+テストされたメソッド。私はあなたに同意しますが、その方法を使用するためだけに余分な依存関係を追加しません。しかし、繰り返しになりますが、私の(大規模な)プロジェクトのほとんどはGuavaまたはApache Commonsのいずれかを使用しています...
Renaud

4
@CorayThan分割して友達を征服する。なぜすでにライブラリによって提供され、テストされているメソッドを書くのですか?私たちは多くのApache CommonsとGuavaを使用していますが、それらは素晴らしいものであり、時間とお金を節約するのに役立ちます。
ステファン

5
ルノーとステファンに同意する。また、ビルドツールを使用している場合、これらのライブラリを含めることは世界で最も簡単です...また、本当に含めたくない場合は、Apache / Guavaコードのソースに移動して、彼らが行ったことをコピーする:すでにそこにある何百もの美しく設計され、テストされたホイールを再発明するためにあなたの時間を無駄にするよりもFARが優れています。
マイクげっ歯類

238

Java 8ではforEachRemainingIteratorインターフェースに追加された新しいメソッドを使用できます。

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

2
どういう意味::?なんて名前?list.add();への直接参照のようです また、java8の新しいものもあるようです。ありがとう!:)
アクエリアスパワー

13
@AquariusPower ::構文はJava 8で新しくなり、ラムダの省略形である「メソッド参照」を参照します。詳細については、こちらを参照してください:docs.oracle.com/javase/tutorial/java/javaOO/…–
Stuart Marks

どこiteratorから来たの?それは未解決のシンボルです
javadba

1
@javadba元の質問は、すでに持っているイテレータを新しいArrayListに変換する方法に関するものでした。この例では、すでに持っているイテレータがと呼ばれると想定されていますiterator
スチュワートマークス

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
問題は、Iterable <Element>ではなく、開始点としてのIterator <Element>についてです。
Jaap、

1
上記のコメントに同意してください。あなたがあなたの名前を付けたことは非常に混乱していますIterable iterator。それらは完全に異なります。
スティーブンハリソン

Java 8 では、を使用してIterator、簡単に使い捨てに 戻すことができます。これは、このような状況で役立ちますが、重要なことをもう一度強調しておきます。この方法は、1 回の使用で問題ない場合にのみ使用できます。複数回呼び出すと、予期しない結果が生じます。上記の答えは、次のようになりますIterable() -> iterator Iterableiterable.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

37
私はそのソリューションを「簡潔」とは言いません。
セルジオ

1
@Sergioそれが私が「かわいい」を書いた理由です。ただし、ローカル変数は必要なく、セミコロンが1つだけ必要です。静的インポートを使用すると短縮できます。
xehpuk 2015

1
私が言うと思いますiterator.forEachRemaining( list::add )また、新しいJava 8で、多くのより簡潔です。リスト変数をにCollectorプッシュしても、a Streamとaでサポートされる必要があるため、この場合の読みやすさは向上しませんSpliterator
Sheepy 2015年

1
@Sergio短くはありませんが、それは大きな違いをもたらす単一の式です。
michaelsnowden

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

11
@LuggiMendoza:Stack Overflowは、問題を切り取って貼り付けて解決できる場所ではありません。これは参考になることを意図しています。これは完全に有益な回答であり、合理的な人なら誰でも、iがイテレータであるとまとめることができます。その音から、あなたは何が起こっているのか理解しようとすることにほとんど努力をしませんでした。
CaTalyst.X 2013

2

CactoosStickyListから試しください:

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

免責事項:私は開発者の一人です。


これは質問の答えにはなりません。Iterable!=Iterator
xehpuk 2017年

すばらしいです。今度は、新しいバージョンのJavaDocを生成してリンクを更新する必要があります。:)
xehpuk 2017年

2

ここにストリームを使用したワンライナーがあります

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

1

私は、一見明白な解決策を指摘したいしません仕事を:

リストリスト= Stream.generate(iterator :: next)
    .collect(Collectors.toList());

これStream#generate(Supplier<T>)は、無限のストリームしか作成できないためで、その引数がスローすることを期待していませんNoSuchElementException(それIterator#next()が最終的に何をするかです)。

イテレーター→ストリーム→リストの方法を選択する場合は、代わりにxehpukの回答を使用する必要があります。



-2

ここで、この場合、可能な限り最速の方法が必要な場合for loopは、その方が適しています。

ループのテイクは10,000 runsテイクのサンプルサイズのイテレータ40 ms2 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

それはあなたの呼び出しです。まだ削除しません。参考になるかもしれません。人々がそれらをdownvoteを開始したとき、私は私の答えをのみを削除します:)
オスカル・ロペス

@ÓscarLópezは正しいと思います...これは必須の回答ではないかもしれませんが、読者(私のような:P)に役立つ情報が含まれています。
Chthonicプロジェクト2013

回答にバグがあります。ではget(int)ループあなただけの1メートルエントリの100Kを見ています。そのループを次のように変更it.size()すると、これらの2つの方法がほぼ同じ速度であることがわかります。一般に、これらの種類のJava速度テストは、実際のパフォーマンス情報が限られているため、懐疑的に見る必要があります。
グレイ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.