Chuyển đổi Iterator thành ArrayList


241

Với Iterator<Element>, làm thế nào chúng ta có thể chuyển đổi đó Iteratorđể ArrayList<Element>(hoặc List<Element>) trong tốt nhất và nhanh nhất cách có thể, để chúng ta có thể sử dụng ArrayList's hoạt động trên đó như get(index), add(element)vv

Câu trả lời:


360

Sử dụng tốt hơn một thư viện như Guava :

import com.google.common.collect.Lists;

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

Một ví dụ khác về ổi:

ImmutableList.copyOf(myIterator);

hoặc Bộ sưu tập Apache Commons :

import org.apache.commons.collections.IteratorUtils;

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

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

8
Tôi không hiểu Theo cách nào thì ArrayList được trả về, giả sử, Guava có tốt hơn ArrayList bình thường không? Họ có làm điều đó một cách hiệu quả hơn? Ngay cả khi nó hiệu quả hơn, nó có thực sự đáng để thêm một phụ thuộc bổ sung (và phức tạp hơn) vào dự án của bạn không?
CorayThan

10
@CorayThan ít mã hơn + phương pháp thử nghiệm. Mặc dù tôi đồng ý với bạn nhưng tôi sẽ không thêm phụ thuộc chỉ để sử dụng phương pháp đó. Nhưng một lần nữa, hầu hết các dự án (lớn) của tôi đều sử dụng Guava hoặc Apache Commons ...
Renaud

4
@CorayThan Chia tay và chinh phục bạn tôi. Tại sao viết một phương thức đã được cung cấp bởi một thư viện và được thử nghiệm? Chúng tôi đang sử dụng rất nhiều Apache Commons và Guava, chúng thật tuyệt vời và giúp bạn tiết kiệm thời gian và tiền bạc.
Stephan

5
Đồng ý với Renaud và Stephan. Ngoài ra, nếu bạn đang sử dụng một công cụ xây dựng, đó là điều dễ nhất trên thế giới để bao gồm các thư viện này ... CSONG ... nếu bạn thực sự không muốn bao gồm chúng, bạn có thể truy cập vào nguồn mã Apache / Guava và sao chép những gì họ đã làm ở đó: FAR tốt hơn là lãng phí thời gian của bạn để phát minh lại hàng trăm bánh xe được thiết kế và thử nghiệm đẹp mắt đã có sẵn ở đó.
mike gặm nhấm

238

Trong Java 8, bạn có thể sử dụng forEachRemainingphương thức mới đã được thêm vào Iteratorgiao diện:

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

2
có nghĩa là ::gì tên đó là gì dường như một tham chiếu trực tiếp đến list.add (); và dường như cũng có một số điều mới java8; và cảm ơn! :)
Sức mạnh của Bảo Bình

13
@AquariusPower ::Cú pháp là mới trong Java 8 và nó đề cập đến một "tham chiếu phương thức", đây là một dạng tốc ký của lambda. Xem tại đây để biết thêm thông tin: docs.oracle.com/javase/tutorial/java/javaOO/ mẹo
Stuart Marks

từ đâu iteratorđến đó là một biểu tượng chưa được giải quyết
javadba

1
@javadba Câu hỏi ban đầu là về cách chuyển đổi một trình vòng lặp mà bạn đã có thành một ArrayList mới. Đối với mục đích của ví dụ này, trình vòng lặp mà bạn đã có được giả sử được gọi iterator.
Stuart Marks

1
Đây phải là câu trả lời được chấp nhận, vì nó là câu trả lời ngắn nhất và không phụ thuộc vào thư viện của bên thứ 3
DanielCuadra

64

Bạn có thể sao chép một iterator vào một danh sách mới như thế này:

Iterator<String> iter = list.iterator();
List<String> copy = new ArrayList<String>();
while (iter.hasNext())
    copy.add(iter.next());

Đó là giả sử rằng danh sách có chứa chuỗi. Thực sự không có cách nào nhanh hơn để tạo lại danh sách từ một trình vòng lặp, bạn bị mắc kẹt với việc duyệt nó bằng tay và sao chép từng phần tử vào một danh sách mới của loại thích hợp.

BIÊN TẬP :

Đây là một phương pháp chung để sao chép một trình vòng lặp vào một danh sách mới theo cách an toàn kiểu:

public static <T> List<T> copyIterator(Iterator<T> iter) {
    List<T> copy = new ArrayList<T>();
    while (iter.hasNext())
        copy.add(iter.next());
    return copy;
}

Sử dụng nó như thế này:

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

Lưu ý có một sự khác biệt giữa IterableIterator.

Nếu bạn có một Iterable, thì với Java 8, bạn có thể sử dụng giải pháp này:

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

Theo tôi biết Collectors.toList()tạo ra ArrayListví dụ.

Trên thực tế theo ý kiến ​​của tôi, nó cũng có vẻ tốt trong một dòng.
Ví dụ: nếu bạn cần quay lại List<Element>từ một số phương thức:

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

4
Câu hỏi là về Iterator <Element> như một điểm khởi đầu, không phải là Iterable <Element>.
Jaap

1
đồng ý với nhận xét trên. siêu khó hiểu rằng bạn đặt tên của bạn Iterable iterator. Chúng hoàn toàn khác nhau.
Stephen Harrison

Trong Java 8, bạn có thể dễ dàng chuyển đổi Iteratortrở lại thành một lần sử dụng Iterable bằng cách sử dụng () -> iterator. Điều này có thể hữu ích trong các tình huống như thế này, nhưng hãy để tôi nhấn mạnh điều quan trọng một lần nữa: Bạn chỉ có thể sử dụng phương pháp này nếu một lần sử dụng Iterable được chấp nhận. Gọi iterable.iterator()nhiều lần sẽ mang lại kết quả bất ngờ. Câu trả lời trên sau đó trở thànhIterator<Element> iterator = createIterator(); List<Element> array = StreamSupport.stream(((Iterable<Element>) () -> iterable).spliterator(), false).collect(toList());
neXus


9

Giải pháp khá súc tích với Java 8 đơn giản bằng cách sử dụng 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)
    );
}

Có cách nào nhỏ gọn hơn để viết này bằng cách sử dụng luồng api không? Có vẻ như không đơn giản hơn cách vòng lặp while bình thường.
xi.lin

37
Tôi sẽ không gọi giải pháp đó là "súc tích".
Sergio

1
@Sergio Đó là lý do tại sao tôi viết "đẹp". Tuy nhiên, nó không cần các biến cục bộ và chỉ có một dấu chấm phẩy. Bạn có thể rút ngắn nó với nhập khẩu tĩnh.
xehpuk

1
Tôi muốn nói iterator.forEachRemaining( list::add ), cũng mới trong Java 8, ngắn gọn hơn nhiều. Đẩy biến danh sách vào Collectorkhông cải thiện khả năng đọc trong trường hợp này, vì nó phải được hỗ trợ bởi a Streamvà a Spliterator.
Sheepy

1
@Sergio Nó không ngắn, nhưng nó là một biểu thức duy nhất, tạo ra sự khác biệt rất lớn.
michaelsnowden

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

11
@LuggiMendoza: Stack Overflow không có nghĩa là nơi bạn có thể cắt và dán và giải quyết vấn đề của mình. Nó có nghĩa là thông tin. Đây là một câu trả lời hoàn hảo về thông tin và bất kỳ người hợp lý nào cũng có thể kết hợp nó với nhau rằng tôi là một người lặp. Từ những âm thanh của nó, bạn gần như không nỗ lực để cố gắng hiểu những gì đang xảy ra.
CaTalyst.X

2

Hãy thử StickyListtừ Cactoos :

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

Tuyên bố miễn trừ trách nhiệm: Tôi là một trong những nhà phát triển.


Điều này không trả lời câu hỏi. Iterable! =Iterator
xehpuk

Tuyệt vời, bây giờ có lẽ bạn cần tạo JavaDoc cho phiên bản mới và cập nhật liên kết. :)
xehpuk

2

Đây là một lớp lót sử dụng Luồng

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

1

Tôi chỉ muốn chỉ ra một giải pháp dường như rõ ràng sẽ KHÔNG hiệu quả:

Danh sách danh sách = Stream.generate (iterator :: next)
    .collect (Collector.toList ());

Đó là bởi vì chỉ Stream#generate(Supplier<T>)có thể tạo ra các luồng vô hạn, nên nó không hy vọng đối số của nó sẽ bị ném NoSuchElementException(đó là điều Iterator#next()sẽ làm cuối cùng).

Thay vào đó, nên sử dụng câu trả lời của xehpuk nếu Iterator → Stream → List list là lựa chọn của bạn.


0

sử dụng ổi google !

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

++


Iterator (không lặp được) cho ArrayList
Dự án Chthonic

-2

Ở đây trong trường hợp này nếu bạn muốn cách nhanh nhất có thể thì for looptốt hơn.

Trình vòng lặp trên một cỡ mẫu 10,000 runssẽ lấy 40 mstrong đó như vòng lặp mất2 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));  

Đó là giả sử rằng danh sách có chứa chuỗi.


3
Chắc chắn sử dụng forvòng lặp và truy cập các phần tử của danh sách get(i)nhanh hơn so với sử dụng trình lặp ... nhưng đó không phải là điều OP yêu cầu, ông đặc biệt đề cập rằng trình lặp được đưa ra làm đầu vào.
Óscar López

@Oscar tôi xin lỗi. Tôi có thể xóa câu trả lời của mình không?
vikiiii

Đó là cuộc gọi của bạn. Tôi sẽ không xóa nó, nó có thể là thông tin. Tôi chỉ xóa câu trả lời của mình khi mọi người bắt đầu hạ thấp chúng :)
Óscar López

Tôi nghĩ @ ÓscarLópez đã đúng ... đây có thể không phải là câu trả lời bắt buộc, nhưng nó chứa thông tin hữu ích cho độc giả (như bản thân tôi: P).
Dự án Chthonic

Bạn có một lỗi trong câu trả lời của bạn. Trong get(int)vòng lặp bạn chỉ nhìn vào 100k trong số 1m mục. Nếu bạn thay đổi vòng lặp đó thành it.size()bạn sẽ thấy rằng 2 phương thức này gần với cùng tốc độ. Nói chung, các loại kiểm tra tốc độ java này cung cấp thông tin hiệu suất thực tế hạn chế và nên được xem xét một cách hoài nghi.
Xám
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.