Programming

반복하는 동안 일반 목록에서 요소를 제거하는 방법은 무엇입니까?

procodes 2020. 2. 20. 23:37
반응형

반복하는 동안 일반 목록에서 요소를 제거하는 방법은 무엇입니까?


각각 처리 해야하는 요소 목록으로 작업하고 결과에 따라 목록에서 제거되는 더 나은 패턴찾고 있습니다.

당신은 사용할 수 없습니다 .Remove(element)돌며 foreach (var element in X)(이 결과 때문에 Collection was modified; enumeration operation may not execute.예외) ... 당신은 또한 사용할 수 없습니다 for (int i = 0; i < elements.Count(); i++).RemoveAt(i)이에 수집 상대적으로 사용자의 현재 위치를 방해하기 때문 i.

이것을 할 수있는 우아한 방법이 있습니까?


for 루프와 반대로 목록을 반복하십시오.

for (int i = safePendingList.Count - 1; i >= 0; i--)
{
    // some code
    // safePendingList.RemoveAt(i);
}

예:

var list = new List<int>(Enumerable.Range(1, 10));
for (int i = list.Count - 1; i >= 0; i--)
{
    if (list[i] > 5)
        list.RemoveAt(i);
}
list.ForEach(i => Console.WriteLine(i));

또는 술어와 함께 RemoveAll 메소드사용하여 다음 을 테스트 할 수 있습니다 .

safePendingList.RemoveAll(item => item.Value == someValue);

다음은 간단한 예입니다.

var list = new List<int>(Enumerable.Range(1, 10));
Console.WriteLine("Before:");
list.ForEach(i => Console.WriteLine(i));
list.RemoveAll(i => i > 5);
Console.WriteLine("After:");
list.ForEach(i => Console.WriteLine(i));

간단하고 간단한 솔루션 :

컬렉션 에서 뒤로 실행되는 표준 for-loop RemoveAt(i)를 사용하고 요소를 제거하십시오.


컬렉션을 반복하면서 컬렉션에서 요소를 제거하려면 역 반복이 가장 먼저 떠 오릅니다.

운 좋게도, 불필요한 입력을 필요로하고 오류가 발생하기 쉬운 for 루프를 작성하는 것보다 더 우아한 솔루션이 있습니다.

ICollection<int> test = new List<int>(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});

foreach (int myInt in test.Reverse<int>())
{
    if (myInt % 2 == 0)
    {
        test.Remove(myInt);
    }
}

 foreach (var item in list.ToList()) {
     list.Remove(item);
 }

이 경우 ".ToList ()"를 추가 지칠대로 지친를 빼고 목록 (또는 LINQ 쿼리의 결과), 직접 "목록"에서 "항목"을 제거 할 수 있습니다에 " 컬렉션 수정; 열거 작업이 실행되지 않을 수 있습니다 ." 오류. 컴파일러는 "목록"의 복사본을 만들어서 어레이에서 안전하게 제거를 수행 할 수 있습니다.

하지만 이 패턴은 매우 효율적이지 않습니다, 그것은 자연적인 느낌을 가지고 있습니다 거의 모든 상황에 유연 . 각 "항목"을 DB에 저장하고 DB 저장에 성공한 경우에만 목록에서 제거하려는 경우와 같이.


일반 목록에서 ToArray ()를 사용하면 일반 목록에서 Remove (item)를 수행 할 수 있습니다.

        List<String> strings = new List<string>() { "a", "b", "c", "d" };
        foreach (string s in strings.ToArray())
        {
            if (s == "b")
                strings.Remove(s);
        }

당신이 요소 선택 않습니다 오히려 당신이 요소를 제거하는 것보다 원하는을 하지 않습니다 싶어합니다. 이것은 요소를 제거하는 것보다 훨씬 쉽습니다 (일반적으로 더 효율적입니다).

var newSequence = (from el in list
                   where el.Something || el.AnotherThing < 0
                   select el);

아래에 Michael Dillon이 남긴 의견에 대한 답변으로 이것을 의견으로 게시하고 싶지만 어쨌든 내 대답에 너무 길어서 아마도 유용합니다.

개인적으로 한 번에 하나씩 항목을 제거하지는 않습니다. 제거가 필요한 경우 호출 RemoveAll하면 술어를 취하고 내부 배열을 한 번만 재정렬하는 반면 제거하는 모든 요소에 Remove대해 Array.Copy작업을 수행합니다. RemoveAll훨씬 더 효율적입니다.

목록을 거꾸로 반복하는 경우 이미 제거하려는 요소의 색인이 있으므로 먼저 목록의 순회를 수행하여 요소의 색인을 찾기 RemoveAt때문에 호출하는 것이 훨씬 효율적 Remove입니다. '제거하려고하지만 이미 해당 색인을 알고 있습니다.

결국, 나는 Removefor-loop를 호출 할 이유가 없습니다 . 이상적으로는 가능한 경우 위 코드를 사용하여 필요에 따라 목록에서 요소를 스트리밍하므로 두 번째 데이터 구조를 전혀 만들지 않아도됩니다.


이 질문에 설명 된대로 .ToList ()를 사용하면 목록이 복사됩니다. ToList ()-새 목록을 작성합니까?

ToList ()를 사용하면 실제로 사본을 반복하므로 원래 목록에서 제거 할 수 있습니다.

foreach (var item in listTracked.ToList()) {    

        if (DetermineIfRequiresRemoval(item)) {
            listTracked.Remove(item)
        }

     }

삭제할 항목을 결정하는 함수에 부작용이없고 항목을 변경하지 않는 경우 (순수한 기능) 간단하고 효율적인 (선형 시간) 솔루션은 다음과 같습니다.

list.RemoveAll(condition);

부작용이 있으면 다음과 같이 사용하십시오.

var toRemove = new HashSet<T>();
foreach(var item in items)
{
     ...
     if(condition)
          toRemove.Add(item);
}
items.RemoveAll(toRemove.Contains);

해시가 양호하다고 가정하면 여전히 선형 시간입니다. 그러나 해시 세트로 인해 메모리 사용이 증가했습니다.

마지막으로 목록이 단지 IList<T>대신에있는 경우이 특별한 foreach 반복자List<T>어떻게 수행 할 수 있습니까? . 이것은 IList<T>다른 많은 답변의 2 차 런타임과 비교하여 일반적인 구현으로 선형 런타임을 갖습니다 .


어떤 조건에서도 제거가 가능하므로

list.RemoveAll(item => item.Value == someValue);

List<T> TheList = new List<T>();

TheList.FindAll(element => element.Satisfies(Condition)).ForEach(element => TheList.Remove(element));

foreach를 사용할 수는 없지만 다음과 같이 항목을 제거 할 때 전달을 반복하고 루프 인덱스 변수를 관리 할 수 ​​있습니다.

for (int i = 0; i < elements.Count; i++)
{
    if (<condition>)
    {
        // Decrement the loop counter to iterate this index again, since later elements will get moved down during the remove operation.
        elements.RemoveAt(i--);
    }
}

일반적으로 이러한 모든 기술은 반복되는 컬렉션의 동작에 의존합니다. 여기에 표시된 기술은 표준 List (T)와 함께 작동합니다. (자신의 컬렉션 클래스를 작성하는 꽤 가능 반복자 그 않는 foreach는 루프 동안 항목을 제거 할 수 있습니다.)


목록을 반복하는 동안 목록에서 Remove또는 RemoveAt목록을 사용 하는 것은 의도적으로 어려워졌습니다. 왜냐하면 거의 항상 잘못된 일 이기 때문입니다 . 영리한 트릭으로 작동시킬 수는 있지만 매우 느릴 것입니다. 전화를 걸 때마다 Remove전체 목록을 스캔하여 제거하려는 요소를 찾아야합니다. 호출 RemoveAt때마다 후속 요소를 1 위치 왼쪽으로 이동해야합니다. 이와 같이, 어떠한 용액을 사용 Remove하거나 RemoveAt, 차 시간을 필요로 할 O (n²) .

가능하면 사용하십시오 RemoveAll. 그렇지 않은 경우 다음 패턴 은 목록 을 선형 시간 O (n) 에서 제자리필터링합니다 .

// Create a list to be filtered
IList<int> elements = new List<int>(new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10});
// Filter the list
int kept = 0;
for (int i = 0; i < elements.Count; i++) {
    // Test whether this is an element that we want to keep.
    if (elements[i] % 3 > 0) {
        // Add it to the list of kept elements.
        elements[kept] = elements[i];
        kept++;
    }
}
// Unfortunately IList has no Resize method. So instead we
// remove the last element of the list until: elements.Count == kept.
while (kept < elements.Count) elements.RemoveAt(elements.Count-1);

나는 소원 은 "패턴"이 같은했다 :

foreach( thing in thingpile )
{
    if( /* condition#1 */ )
    {
        foreach.markfordeleting( thing );
    }
    elseif( /* condition#2 */ )
    {
        foreach.markforkeeping( thing );
    }
} 
foreachcompleted
{
    // then the programmer's choices would be:

    // delete everything that was marked for deleting
    foreach.deletenow(thingpile); 

    // ...or... keep only things that were marked for keeping
    foreach.keepnow(thingpile);

    // ...or even... make a new list of the unmarked items
    others = foreach.unmarked(thingpile);   
}

이것은 프로그래머의 두뇌에서 진행되는 프로세스와 코드를 정렬합니다.


유지하고 싶지 않은 요소를 필터링 한 LINQ 쿼리에서 목록을 다시 할당합니다.

list = list.Where(item => ...).ToList();

목록이 너무 크지 않으면이 작업을 수행 할 때 심각한 성능 문제가 없어야합니다.


목록을 반복하면서 목록에서 항목을 제거하는 가장 좋은 방법은를 사용하는 것 RemoveAll()입니다. 그러나 사람들이 작성한 주요 관심사는 루프 내부에서 복잡한 작업을 수행하거나 복잡한 비교 사례를 가져야한다는 것입니다.

해결책은 여전히 ​​사용 RemoveAll()하지만 다음 표기법을 사용하는 것입니다.

var list = new List<int>(Enumerable.Range(1, 10));
list.RemoveAll(item => 
{
    // Do some complex operations here
    // Or even some operations on the items
    SomeFunction(item);
    // In the end return true if the item is to be removed. False otherwise
    return item > 5;
});

술어 가 요소의 부울 특성 이라고 가정하면 true이면 요소를 제거해야합니다.

        int i = 0;
        while (i < list.Count())
        {
            if (list[i].predicate == true)
            {
                list.RemoveAt(i);
                continue;
            }
            i++;
        }

foreach(var item in list.ToList())

{

if(item.Delete) list.Remove(item);

}

첫 번째 목록에서 완전히 새로운 목록을 만들면됩니다. 완전히 새로운 목록을 만들면 아마도 이전 방법에 비해 성능이 뛰어 나기 때문에 "Right"가 아닌 "Easy"라고 말합니다 (벤치 마법에 신경 쓰지 않았습니다). 일반적으로이 패턴을 선호합니다. 극복에도 유용 할 수 있습니다. Linq-to-Entities 제한 사항.

for(i = list.Count()-1;i>=0;i--)

{

item=list[i];

if (item.Delete) list.Remove(item);

}

이 방법은 일반 오래된 For 루프를 사용하여 목록을 거꾸로 순환합니다. 컬렉션의 크기가 변경되면이 작업을 앞뒤로 수행하는 것이 문제가 될 수 있지만 항상 안전해야합니다.


나는 주어진 상황에서 모든 n 번째 요소 를 제거 해야하는 비슷한 상황 에 처했다 List<T>.

for (int i = 0, j = 0, n = 3; i < list.Count; i++)
{
    if ((j + 1) % n == 0) //Check current iteration is at the nth interval
    {
        list.RemoveAt(i);
        j++; //This extra addition is necessary. Without it j will wrap
             //down to zero, which will throw off our index.
    }
    j++; //This will always advance the j counter
}

목록에서 항목을 제거하는 비용은 제거 할 항목 다음에 오는 항목 수에 비례합니다. 품목의 전반부에서 제거 할 수있는 자격이있는 경우, 품목을 개별적으로 제거하는 방법에 따라 N * N / 4 항목 복사 작업을 수행해야하므로 목록이 크면 비용이 매우 많이들 수 있습니다 .

더 빠른 방법은 목록을 스캔하여 제거 할 첫 번째 항목 (있는 경우)을 찾은 다음 해당 항목부터 해당 항목을 해당 지점에 보관해야합니다. 이 작업이 완료되면 R 항목을 유지해야하는 경우 목록의 첫 번째 R 항목이 해당 R 항목이되고 삭제가 필요한 모든 항목이 끝납니다. 해당 항목을 역순으로 삭제하면 시스템에서 해당 항목을 복사하지 않아도되므로 목록에 첫 번째 F를 포함하여 R 항목이 모두 N 개있는 항목이 있으면 목록을 유지해야합니다. RF 항목을 복사하고 목록을 한 항목 NR 번 줄입니다. 모든 선형 시간.


내 접근 방식은 먼저 인덱스 목록을 만들고 삭제해야합니다. 그런 다음 색인을 반복하고 초기 목록에서 항목을 제거합니다. 이것은 다음과 같습니다

var messageList = ...;
// Restrict your list to certain criteria
var customMessageList = messageList.FindAll(m => m.UserId == someId);

if (customMessageList != null && customMessageList.Count > 0)
{
    // Create list with positions in origin list
    List<int> positionList = new List<int>();
    foreach (var message in customMessageList)
    {
        var position = messageList.FindIndex(m => m.MessageId == message.MessageId);
        if (position != -1)
            positionList.Add(position);
    }
    // To be able to remove the items in the origin list, we do it backwards
    // so that the order of indices stays the same
    positionList = positionList.OrderByDescending(p => p).ToList();
    foreach (var position in positionList)
    {
        messageList.RemoveAt(position);
    }
}

반복하는 목록을 복사하십시오. 그런 다음 복사본에서 제거하고 원본을 중재하십시오. 거꾸로 돌아가는 것은 혼란스럽고 병렬로 반복 할 때 제대로 작동하지 않습니다.

var ids = new List<int> { 1, 2, 3, 4 };
var iterableIds = ids.ToList();

Parallel.ForEach(iterableIds, id =>
{
    ids.Remove(id);
});

C #에서 쉬운 방법은 삭제하려는 항목을 표시 한 다음 반복 할 새 목록을 만드는 것입니다 ...

foreach(var item in list.ToList()){if(item.Delete) list.Remove(item);}  

또는 더 간단한 사용 linq ....

list.RemoveAll(p=>p.Delete);

그러나 다른 작업이나 스레드가 제거하는 동안 동시에 동일한 목록에 액세스하고 ConcurrentList를 사용할 수 있는지 고려할 가치가 있습니다.


특성으로 제거 할 요소를 추적하고 프로세스 후에 모두 제거하십시오.

using System.Linq;

List<MyProperty> _Group = new List<MyProperty>();
// ... add elements

bool cond = true;
foreach (MyProperty currObj in _Group)
{
    if (cond) 
    {
        // SET - element can be deleted
        currObj.REMOVE_ME = true;
    }
}
// RESET
_Group.RemoveAll(r => r.REMOVE_ME);

누군가에게 도움이되는 경우 2 센트를 여기에 추가하고 싶었습니다. 비슷한 문제가 있었지만 반복되는 동안 배열 목록에서 여러 요소를 제거해야했습니다. 오류가 발생하여 여러 요소가 제거되었지만 루프의 색인이 유지되지 않았기 때문에 색인이 배열 목록의 크기보다 큽니다. 그 추적. 나는 간단한 점검으로 이것을 고쳤다.

ArrayList place_holder = new ArrayList();
place_holder.Add("1");
place_holder.Add("2");
place_holder.Add("3");
place_holder.Add("4");

for(int i = place_holder.Count-1; i>= 0; i--){
    if(i>= place_holder.Count){
        i = place_holder.Count-1; 
    }

// some method that removes multiple elements here
}

myList.RemoveAt(i--);

simples;

참고 URL : https://stackoverflow.com/questions/1582285/how-to-remove-elements-from-a-generic-list-while-iterating-over-it



반응형