แปลง Iterator เป็น ArrayList


241

รับIterator<Element>แล้วเราจะแปลงIteratorให้เป็นArrayList<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 :

import org.apache.commons.collections.IteratorUtils;

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

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

8
ฉันไม่เข้าใจ ArrayList ส่งคืนโดยวิธีใด Guava ดีกว่า ArrayList ปกติอย่างไร พวกเขาทำอย่างมีประสิทธิภาพมากขึ้น? แม้ว่ามันจะมีประสิทธิภาพมากกว่ามันคุ้มที่จะเพิ่มการพึ่งพา (และความซับซ้อนมากขึ้น) ในโครงการของคุณหรือไม่
CorayThan

10
@CorayThan รหัสน้อย + วิธีการทดสอบ แม้ว่าฉันจะเห็นด้วยกับคุณฉันจะไม่เพิ่มการพึ่งพาพิเศษเพียงเพื่อใช้วิธีการนั้น แต่แล้วอีกครั้งส่วนใหญ่ของโครงการของฉัน (ขนาดใหญ่) ใช้ทั้งฝรั่งหรือ Apache Commons ...
Renaud

4
@CorayThan แบ่งและพิชิตเพื่อนของฉัน เหตุใดจึงต้องเขียนวิธีการที่ห้องสมุดจัดไว้ให้และทดสอบแล้ว? เราใช้ Apache Commons และ Guava จำนวนมากพวกมันยอดเยี่ยมมากและช่วยให้คุณประหยัดเวลาและเงิน
เตฟาน

5
เห็นด้วยกับ Renaud และ Stephan นอกจากนี้หากคุณใช้เครื่องมือสร้างมันเป็นสิ่งที่ง่ายที่สุดในโลกที่จะรวมไลบรารีเหล่านี้ ... นอกจากนี้ ... หากคุณไม่ต้องการรวมไว้คุณสามารถไปยังแหล่งที่มาของรหัส Apache / Guava และ คัดลอกสิ่งที่พวกเขาทำที่นั่น: ไกลกว่าเสียเวลาไปกับการสร้างล้อที่ได้รับการออกแบบทางวิศวกรรมและการทดสอบที่สวยงามซึ่งมีอยู่จำนวนมากแล้ว
ไมค์หนู

238

ใน Java 8 คุณสามารถใช้forEachRemainingวิธีการใหม่ที่ถูกเพิ่มเข้ากับIteratorอินเทอร์เฟซ:

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

2
แปลว่า::อะไร ชื่ออะไร ดูเหมือนว่าการอ้างอิงโดยตรงไปยัง list.add (); และดูเหมือนว่าบางสิ่งใหม่ java8; และขอบคุณ! :)
กุมภ์ Power

13
@AquariusPower ::ไวยากรณ์ใหม่ใน Java 8 และหมายถึง "การอ้างอิงเมธอด" ซึ่งเป็นรูปแบบย่อของแลมบ์ดา ดูที่นี่สำหรับข้อมูลเพิ่มเติม: docs.oracle.com/javase/tutorial/java/javaOO/ …
Stuart Marks

ที่จะiteratorมาจากไหน มันเป็นสัญลักษณ์ที่ไม่สามารถแก้ไขได้
javadba

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ซุปเปอร์สับสนว่าคุณชื่อของคุณ พวกเขาแตกต่างอย่างสิ้นเชิง
สตีเฟ่นแฮร์ริสัน

ใน 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

21

นอกจากนี้คุณยังสามารถใช้IteratorUtilsจาก Apache คอมมอนส์คอลเลกชันแม้ว่ามันจะไม่สนับสนุน generics:

List list = IteratorUtils.toList(iterator);

นอกจากนี้ยังมีวิธีการ 2 toArray และ 1 ยอมรับประเภท: commons.apache.org/proper/commons-collections/javadocs/… , java.lang.Class)
dwana

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

มีวิธีที่กะทัดรัดกว่านี้ในการเขียนโดยใช้ stream api หรือไม่ ดูเหมือนจะไม่ง่ายกว่าปกติในขณะที่วิธีการวนรอบ
xi.lin

37
ฉันจะไม่เรียกวิธีแก้ปัญหานั้นว่า "รัดกุม"
Sergio

1
@Sergio นั่นเป็นเหตุผลที่ฉันเขียนว่า "สวย" อย่างไรก็ตามไม่จำเป็นต้องใช้ตัวแปรโลคัลและเซมิโคลอนเพียงอันเดียว คุณอาจย่อให้สั้นลงด้วยการนำเข้าแบบคงที่
xehpuk

1
ฉันจะบอกว่าiterator.forEachRemaining( list::add )ใหม่ใน Java 8 มีความกระชับมากขึ้น ผลักดันเข้าสู่ตัวแปรรายการCollectorไม่ได้ปรับปรุง readibility ในกรณีนี้เพราะมันจะต้องมีการสนับสนุนจากและStream Spliterator
Sheepy

1
@Sergio มันไม่สั้น แต่มันเป็นสำนวนเดียว
michaelsnowden

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

11
@LuggiMendoza: Stack Overflow ไม่ได้หมายถึงสถานที่ที่คุณสามารถตัดและวางและแก้ปัญหาของคุณได้ มันมีความหมายที่จะให้ข้อมูล นี่เป็นคำตอบที่ให้ข้อมูลอย่างสมบูรณ์แบบและบุคคลที่มีเหตุผลควรจะสามารถรวบรวมมันไว้ด้วยกันว่าฉันเป็นตัววนซ้ำ จากเสียงของมันคุณแทบไม่ต้องพยายามทำความเข้าใจกับสิ่งที่เกิดขึ้น
CaTalyst.X

2

ลอง StickyListจากCactoos :

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

1

ฉันแค่ต้องการชี้ให้เห็นทางออกที่ชัดเจนที่ดูเหมือนจะไม่ทำงาน:

รายการรายการ = Stream.generate (ตัววนซ้ำ :: ถัดไป)
    .collect (Collectors.toList ());

นั่นเป็นเพราะStream#generate(Supplier<T>)สามารถสร้างกระแสข้อมูลที่ไม่มีที่สิ้นสุดเท่านั้นมันไม่ได้คาดหวังว่าอาร์กิวเมนต์จะโยนNoSuchElementException(นั่นคือสิ่งที่Iterator#next()จะทำในท้ายที่สุด)

คำตอบของ xehpukควรใช้แทนหาก Iterator → Stream → List ทางเป็นทางเลือกของคุณ



-2

ที่นี่ในกรณีนี้ถ้าคุณต้องการวิธีที่เร็วที่สุดที่เป็นไปได้แล้วfor loopจะดีกว่า

ตัววนซ้ำในขนาดตัวอย่างของการ10,000 runsทำ40 msหน้าที่เป็นที่สำหรับการวนซ้ำ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)จะเร็วกว่าการใช้ iterator ... แต่นั่นไม่ใช่สิ่งที่ OP ก็ถามเขากล่าวถึงเฉพาะที่iteratorจะได้รับเป็น input
ÓscarLópez

@Oscar ฉันขอโทษฉันควรจะลบคำตอบของฉัน?
vikiiii

นั่นคือการโทรของคุณ ฉันจะไม่ลบมันอาจเป็นข้อมูล ฉันเพียงลบคำตอบของฉันเมื่อผู้คนเริ่มโหวตพวกเขา :)
--scar López

ฉันคิดว่า @ ÓscarLópezถูกต้อง ... นี่อาจไม่ใช่คำตอบที่จำเป็น แต่มีข้อมูลที่เป็นประโยชน์สำหรับผู้อ่าน (เช่นตัวฉัน: P)
โครงการ Chthonic

คุณมีข้อบกพร่องในคำตอบของคุณ ในget(int)ลูปคุณจะดูที่ 100k ของ 1m รายการเท่านั้น หากคุณเปลี่ยนลูปนั้นเป็นit.size()คุณจะเห็นว่า 2 วิธีนี้ใกล้เคียงกับความเร็วเดียวกัน โดยทั่วไปการทดสอบความเร็วจาวาประเภทนี้จะให้ข้อมูลประสิทธิภาพการทำงานในชีวิตจริงที่ จำกัด และควรดูด้วยความสงสัย
สีเทา
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.