반복자를 ArrayList로 변환


241

을 감안할 때 Iterator<Element>, 우리가 그것을 변환 할 수있는 방법 IteratorArrayList<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.