[java] 반복 중에 컬렉션에 요소 추가

컬렉션을 반복하는 동안 컬렉션에 요소를 추가 할 수 있습니까?

보다 구체적으로, 컬렉션을 반복하고 요소가 특정 조건을 충족하면 컬렉션에 다른 요소를 추가하고 이러한 추가 된 요소도 반복되는지 확인합니다. (나는 이것이 끝없는 루프로 이어질 있다는 것을 알고 있지만 내 경우에는 그렇지 않을 것이라고 확신합니다.)

자바 튜토리얼 썬이 불가능합니다 제안 : “주 Iterator.remove입니다 . 반복하는 동안 컬렉션을 수정할 수있는 안전한 방법, 반복이 진행되는 동안 기본 컬렉션 다른 방법으로 변경되었을 경우의 동작은 정의되어 있지 않기 때문에이”

반복기를 사용하여 원하는 작업을 수행 할 수없는 경우 어떻게 하시겠습니까?



답변

반복하려는 요소로 큐를 구축하는 것은 어떻습니까? 요소를 추가하려면 큐 끝에 큐에 넣고 큐가 비워 질 때까지 요소를 계속 제거하십시오. 이것이 일반적으로 폭 우선 검색이 작동하는 방식입니다.


답변

여기에는 두 가지 문제가 있습니다.

첫 번째 문제는에 추가 Collection하는 것 Iterator입니다. 언급 Collection했듯이에 대한 설명서에 명시된대로 기본 이 수정 될 때 정의 된 동작이 없습니다 Iterator.remove.

…이 메서드를 호출하는 것 이외의 방법으로 반복이 진행되는 동안 기본 컬렉션이 수정되면 반복기의 동작이 지정되지 않습니다.

두 번째 문제 는를 Iterator획득 할 수 있고 동일한 요소로 돌아가 Iterator더라도 Collection.iterator메서드 문서에 언급 된대로 반복 순서에 대한 보장이 없다는 것입니다 .

… 요소가 반환되는 순서에 대한 보장이 없습니다 (이 컬렉션이 보장을 제공하는 일부 클래스의 인스턴스가 아닌 경우).

예를 들어 목록이 있다고 가정 해 보겠습니다 [1, 2, 3, 4].

하자 말은 5(가) 때 추가 된 Iterator있었다 3, 어떻게 든, 우리는 얻을 Iterator에서 반복을 재개 할 것을 4. 그러나 5이후에 올 보장은 없습니다 4. 반복 순서는 [5, 1, 2, 3, 4]다음과 같을 수 있습니다 . 그러면 반복기는 여전히 요소를 놓칠 것 5입니다.

행동에 대한 보장이 없기 때문에 어떤 일이 일어날 것이라고 추측 할 수 없습니다.

한 가지 대안은 Collection새로 생성 된 요소를 추가 할 수 있는 별도의 요소를 만든 다음 해당 요소를 반복하는 것입니다.

Collection<String> list = Arrays.asList(new String[]{"Hello", "World!"});
Collection<String> additionalList = new ArrayList<String>();

for (String s : list) {
    // Found a need to add a new element to iterate over,
    // so add it to another list that will be iterated later:
    additionalList.add(s);
}

for (String s : additionalList) {
    // Iterate over the elements that needs to be iterated over:
    System.out.println(s);
}

편집하다

Avi의 대답 에 대해 자세히 설명 하면 반복하려는 요소를 대기열에 넣고 대기열에 요소가있는 동안 요소를 제거 할 수 있습니다. 이렇게하면 원래 요소 외에 새 요소에 대한 “반복”이 허용됩니다.

어떻게 작동하는지 살펴 보겠습니다.

개념적으로 큐에 다음 요소가있는 경우 :

[1, 2, 3, 4]

그리고을 제거 할 때 1를 추가하기로 결정 42하면 대기열은 다음과 같습니다.

[2, 3, 4, 42]

큐가 FIFO ( 선입 선출 ) 데이터 구조이므로이 순서는 일반적입니다. ( Queue인터페이스 에 대한 문서에서 언급했듯이 , 이것은 반드시 필요한 것은 아닙니다.Queue . . PriorityQueue요소를 자연스러운 순서로 정렬하는 경우 FIFO가 아닙니다.)

다음은 사용 예이다 LinkedList(a 인 Queuedequeing 동안 첨가 추가 요소와 함께 모든 요소를 통과하기 위해). 위의 예와 유사하게 42요소 2가 제거 되면 요소 가 추가 됩니다.

Queue<Integer> queue = new LinkedList<Integer>();
queue.add(1);
queue.add(2);
queue.add(3);
queue.add(4);

while (!queue.isEmpty()) {
    Integer i = queue.remove();
    if (i == 2)
        queue.add(42);

    System.out.println(i);
}

결과는 다음과 같습니다.

1
2
3
4
42

희망대로 42맞았을 때 추가 된 요소 가 2나타났습니다.


답변

또한처럼, 더 전문 유형의 일부를보고 할 수 있습니다 반복자 , 해 NavigableSet (당신이지도에 관심이있는 경우) 하는 NavigableMap를 .


답변

사실 그것은 다소 쉽습니다. 최적의 방법을 생각하십시오. 최적의 방법은 다음과 같습니다.

for (int i=0; i<list.size(); i++) {
   Level obj = list.get(i);

   //Here execute yr code that may add / or may not add new element(s)
   //...

   i=list.indexOf(obj);
}

다음 예제는 반복 요소 이전에 추가 된 새 요소를 반복 할 필요가없는 가장 논리적 인 경우에 완벽하게 작동합니다. 반복 요소 이후에 추가 된 요소에 대해-반복하지 않을 수도 있습니다. 이 경우 단순히 반복하지 않도록 표시하는 플래그를 사용하여 yr 객체를 추가 / 또는 확장해야합니다.


답변

다음 ListIterator과 같이 사용하십시오 .

List<String> l = new ArrayList<>();
l.add("Foo");
ListIterator<String> iter = l.listIterator(l.size());
while(iter.hasPrevious()){
    String prev=iter.previous();
    if(true /*You condition here*/){
        iter.add("Bah");
        iter.add("Etc");
    }
}

핵심은 역순 으로 반복하는 것입니다. 그러면 추가 된 요소가 다음 반복에 나타납니다.


답변

꽤 오래되었다는 것을 알고 있습니다. 그러나 다른 누구에게도 쓸모가 있다고 생각했습니다. 최근에 반복 중에 수정할 수있는 대기열이 필요한 유사한 문제를 발견했습니다. 나는 listIterator를 사용하여 Avi가 제안한-> Avi ‘s Answer 와 같은 줄에서 동일한 것을 구현했습니다 . 이것이 귀하의 필요에 적합한 지 확인하십시오.

ModifyWhileIterateQueue.java

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class ModifyWhileIterateQueue<T> {
        ListIterator<T> listIterator;
        int frontIndex;
        List<T> list;

        public ModifyWhileIterateQueue() {
                frontIndex = 0;
                list =  new ArrayList<T>();
                listIterator = list.listIterator();
        }

        public boolean hasUnservicedItems () {
                return frontIndex < list.size();
        }

        public T deQueue() {
                if (frontIndex >= list.size()) {
                        return null;
                }
                return list.get(frontIndex++);
        }

        public void enQueue(T t) {
                listIterator.add(t);
        }

        public List<T> getUnservicedItems() {
                return list.subList(frontIndex, list.size());
        }

        public List<T> getAllItems() {
                return list;
        }
}

ModifyWhileIterateQueueTest.java

    @Test
    public final void testModifyWhileIterate() {
            ModifyWhileIterateQueue<String> queue = new ModifyWhileIterateQueue<String>();
            queue.enQueue("one");
            queue.enQueue("two");
            queue.enQueue("three");

            for (int i=0; i< queue.getAllItems().size(); i++) {
                    if (i==1) {
                            queue.enQueue("four");
                    }
            }

            assertEquals(true, queue.hasUnservicedItems());
            assertEquals ("[one, two, three, four]", ""+ queue.getUnservicedItems());
            assertEquals ("[one, two, three, four]", ""+queue.getAllItems());
            assertEquals("one", queue.deQueue());

    }


답변

반복자를 사용하여 … 아니, 그렇게 생각하지 않습니다. 다음과 같이 함께 해킹해야합니다.

    Collection< String > collection = new ArrayList< String >( Arrays.asList( "foo", "bar", "baz" ) );
    int i = 0;
    while ( i < collection.size() ) {

        String curItem = collection.toArray( new String[ collection.size() ] )[ i ];
        if ( curItem.equals( "foo" ) ) {
            collection.add( "added-item-1" );
        }
        if ( curItem.equals( "added-item-1" ) ) {
            collection.add( "added-item-2" );
        }

        i++;
    }

    System.out.println( collection );

어떤 소리 :
[foo, bar, baz, added-item-1, added-item-2]