컬렉션을 반복하는 동안 컬렉션에 요소를 추가 할 수 있습니까?
보다 구체적으로, 컬렉션을 반복하고 요소가 특정 조건을 충족하면 컬렉션에 다른 요소를 추가하고 이러한 추가 된 요소도 반복되는지 확인합니다. (나는 이것이 끝없는 루프로 이어질 수 있다는 것을 알고 있지만 내 경우에는 그렇지 않을 것이라고 확신합니다.)
자바 튜토리얼 썬이 불가능합니다 제안 : “주 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 인 Queue
dequeing 동안 첨가 추가 요소와 함께 모든 요소를 통과하기 위해). 위의 예와 유사하게 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]