반복자를 ArrayList로 변환


241

을 감안할 때 Iterator<Element>, 우리가 그것을 변환 할 수있는 방법 Iterator에 ArrayList<Element>(또는 List<Element>에서) 가장 빠른 방법은 가능한, 그래서 우리가 사용할 수있는 ArrayList이 같은에들에게 '작업을 get(index), add(element)등

답변:


360

구아바 와 같은 라이브러리를 더 잘 사용하십시오 .

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
나는 그것을 얻지 못한다. 예를 들어 Guava가 ArrayList를 반환하는 방법은 일반적인 ArrayList보다 낫습니까? 그들은 더 효율적인 방식으로합니까? 더 효율적이더라도 프로젝트에 추가 의존성과 복잡성을 더할 가치가 있습니까?
— CorayThan

10
@Coray보다 적은 코드 + 테스트 방법. 나는 당신에게 동의하지만 그 방법을 사용하기 위해 여분의 의존성을 추가하지는 않을 것입니다. 그러나 대부분의 (대규모) 프로젝트는 Guava 또는 Apache Commons를 사용합니다.
— Renaud

4
@CorayThan 나누고 내 친구를 정복하십시오. 라이브러리에서 이미 제공하고 테스트 한 메소드를 작성하는 이유는 무엇입니까? 우리는 많은 Apache Commons와 Guava를 사용하고 있습니다. 대단하고 시간과 돈을 절약 할 수 있습니다.
— Stephan

5
Renaud와 Stephan에 동의하십시오. 또한 빌드 도구를 사용하는 경우 전 세계에서 이러한 라이브러리를 포함하는 것이 가장 쉬운 방법입니다 ... 또한 ... 포함하지 않으려는 경우 Apache / Guava 코드 소스로 이동하여 그들이 한 일을 복사하십시오 : 이미 아름답게 설계되고 테스트 된 수백 개의 바퀴를 재창조하는 것보다 시간을 낭비하는 것보다 낫습니다.
— 마이크 설치류

238

Java 8에서는 인터페이스 forEachRemaining에 추가 된 새로운 메소드를 사용할 수 있습니다 Iterator.

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

2
무슨 뜻 ::입니까? 어떤 이름이 있습니까? list.add ()에 대한 직접적인 참조 인 것 같습니다. 또한 java8의 새로운 것 같습니다. 그리고 감사합니다! :)
— Aquarius Power

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, 다음 자바 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. 그들은 완전히 다릅니다.
— Stephen Harrison

자바 8에서는 쉽게 변환 할 수 있습니다 IteratorA와 다시 하나의 사용을 Iterable 사용하여 () -> iterator. 이것은 이와 같은 상황에서 유용 할 수 있지만 중요한 사항을 다시 강조하겠습니다 . 한 번만 사용할 수있는 경우에만이 방법을 사용할 수 있습니다 Iterable. iterable.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
나는 그 솔루션을 "간결한"것으로 부르지 않을 것이다.
— Sergio

1
@Sergio 그래서 "예쁜"썼다. 그러나 지역 변수는 필요하지 않으며 세미콜론은 하나만 필요합니다. 정적 가져 오기로 단축 할 수 있습니다.
— xehpuk

1
나는 말할 것 iterator.forEachRemaining( list::add )또한 새로운 자바 8, 훨씬 더 간결. Collector이 경우 리스트 변수를 푸시 해도 가독성이 향상되지 않습니다 . a Stream및 a 가 지원해야하기 때문 Spliterator입니다.
— Sheepy

1
@Sergio 짧지는 않지만 단일 표현이므로 큰 차이가 있습니다.
— michaelsnowden

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

11
@LuggiMendoza : 스택 오버플로는 문제를 잘라 붙여 넣을 수있는 장소가 아닙니다. 유익한 정보입니다. 이것은 완벽하게 유익한 답변이며 합리적인 사람은 i가 반복 자라는 것을 함께 정리할 수 있어야합니다. 그것의 소리에서, 당신은 무슨 일이 일어나고 있는지 이해하려고 노력하지 않습니다.
— CaTalyst.X

2

CactoosStickyList 에서 시도하십시오 :

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 (iterator :: next)
    .collect (Collectors.toList ());

Stream#generate(Supplier<T>)무한 스트림 만 만들 수 있기 때문에 인수가 발생할 것으로 기대하지 않습니다 NoSuchElementException( Iterator#next()결국 수행 할 작업).

Iterator → Stream → List 방식을 선택하는 경우 xehpuk의 답변 을 대신 사용해야합니다.



-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)것이 반복자를 사용하는 것보다 빠르지 만 OP가 요구 한 것은 아닙니다. 그는 반복자 가 입력으로 제공 된다고 구체적으로 언급했습니다 .
— Óscar López

@Oscar 죄송합니다. 답변을 삭제해야합니까?
— vikiiii

그게 당신의 전화입니다. 아직 삭제하지 않았으므로 유익 할 수 있습니다. 사람들이 :)을 downvote하기 시작할 때 나는 단지 내 답변을 삭제
— 오스카르 로페스에게

@ ÓscarLópez가 옳다고 생각합니다 ...이 답변은 아닐 수도 있지만 독자에게 유용한 정보가 포함되어 있습니다 (예 : 나 자신 : P).
— Chthonic Project

답변에 버그가 있습니다. get(int)루프 에서는 1m 항목 중 100k 만보고 있습니다. 루프를 변경 it.size()하면이 두 가지 방법이 같은 속도에 가깝다는 것을 알 수 있습니다. 일반적으로 이러한 종류의 Java 속도 테스트는 제한된 실제 성능 정보를 제공하며 회의적으로 살펴 봐야합니다.
— Gray
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.