删除列表中的项目,同时使用每个循环进行迭代

时间:2013-10-08 15:43:21

标签: vb.net list loops foreach

我有一个名为NeededList的列表我需要检查此列表中的每个项目以查看它是否存在于我的数据库中。如果它确实存在于数据库中,我需要将其从列表中删除。但是当我迭代它时,我无法更改列表。我怎样才能做到这一点?

到目前为止,这是我的代码:

For Each Needed In NeededList
        Dim Ticker = Needed.Split("-")(0).Trim()
        Dim Year = Needed.Split("-")(1).Trim()
        Dim Period = Needed.Split("-")(2).Trim()
        Dim Table = Needed.Split("-")(3).Trim()
        Dim dr As OleDbDataReader
        Dim cmd2 As New OleDb.OleDbCommand("SELECT * FROM " & Table & " WHERE Ticker = ? AND [Year] = ? AND Period = ?", con)
        cmd2.Parameters.AddWithValue("?", Ticker)
        cmd2.Parameters.AddWithValue("?", Year)
        cmd2.Parameters.AddWithValue("?", Period)
        dr = cmd2.ExecuteReader
        If dr.HasRows Then
            NeededList.Remove(Needed)
        End If
Next

8 个答案:

答案 0 :(得分:33)

不,你不能使用for each,但你可以使用旧式的循环来做到这一点 诀窍是从结束开始并向后循环。

For x = NeededList.Count - 1 to 0 Step -1
    ' Get the element to evaluate....
    Dim Needed = NeededList(x)
    .....
    If dr.HasRows Then
        NeededList.RemoveAt(x)
    End If
Next

您需要以这种方式接近循环,因为您不会有跳过元素的风险,因为当前的元素已被删除。

例如,假设您删除了集合中的第四个元素,之后,第五个元素成为第四个元素。但随后索引器上升到5.这样,前一个第五个元素(现在位于第四个位置)永远不会被评估。当然,您可以尝试更改索引器的值,但这总是在错误的代码和错误等待发生。

答案 1 :(得分:14)

保持安全并使用ToList()制作副本:

For Each Needed In NeededList.ToList()
    Dim Ticker = Needed.Split("-")(0).Trim()
    ...
    If dr.HasRows Then
        NeededList.Remove(Needed)
    End If
Next

答案 2 :(得分:4)

您可以使用For循环迭代每个索引,步骤-1。

For i as Integer = NeededList.Count - 1 to 0 Step -1

    Dim Needed = NeededList(i)

    'this is a copy of your code
    Dim Ticker = Needed.Split("-")(0).Trim()
    Dim Year = Needed.Split("-")(1).Trim()
    Dim Period = Needed.Split("-")(2).Trim()
    Dim Table = Needed.Split("-")(3).Trim()

    Dim dr As OleDbDataReader
    Dim cmd2 As New OleDb.OleDbCommand("SELECT * FROM " & Table & " WHERE Ticker = ? AND [Year] = ? AND Period = ?", con)
    cmd2.Parameters.AddWithValue("?", Ticker)
    cmd2.Parameters.AddWithValue("?", Year)
    cmd2.Parameters.AddWithValue("?", Period)
    dr = cmd2.ExecuteReader

    'MODIFIED CODE
    If dr.HasRows Then NeededList.RemoveAt(i)

Next i

答案 3 :(得分:3)

使用For Each循环无法修改数组的内容(或者您可以使用For Each快速枚举的任何其他内容。您需要使用简单的For循环并迭代每个指数。

提示:因为你要删除索引,我建议从最后一个索引开始,然后按照第一个索引的方式进行操作,这样每次删除索引时都不会超过一个。

答案 4 :(得分:0)

不,您无法从正在处理的列表中删除,例如 For Each Str As String in listOfStrings                     如果是Str.Equals(" Pat")那么                         Dim index = listOfStrings.IndexOf(Str)                         listOfStrings .RemoveAt(index)                     万一                 下一步

但是这种方法可以复制您的列表并从中删除,例如 For Each Str As String in listOfStrings                     如果是Str.Equals(" Pat")那么                         Dim index = listOfStringsCopy.IndexOf(Str)                         listOfStringsCopy.RemoveAt(指数)                     万一                 下一步

答案 5 :(得分:0)

您也可以反转列表元素的顺序,并仍然使用For EachIEnumerable Cast扩展程序使用Reverse

使用List(Of String)的简单示例:

For Each Needed In NeededList.Cast(Of List(Of String)).Reverse()
    If dr.HasRows Then
        NeededList.Remove(Needed)
    End If
Next

答案 6 :(得分:0)

这个怎么样(不需要迭代):

NeededList = (NeededList.Where(Function(Needed) IsNeeded(Needed)).ToList

Function IsNeeded(Needed As ...) As Boolean
    ...
    Return Not dr.HasRows
End Function

答案 7 :(得分:0)

由于列表从末尾开始增长和缩小,因此可以通过按 反向 顺序遍历列表来解决问题。


理论:

反转集合比返回副本更快。 因此,如果需要提高速度,请在处理集合之前使用list.Reverse()。


经测试的性能:

ReverseToList:   00:00:00.0005484
CopyWithToList:  00:00:00.0017638
CopyWithForeach: 00:00:00.0141009

实施:

For Each Needed In NeededList.Reverse()
    Dim Ticker = Needed.Split("-")(0).Trim()
    '...
    If dr.HasRows Then
        NeededList.Remove(Needed)
    End If
Next

下面的代码用于测试方法的性能:

using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;

public class Program
{
    public static void Main()
    {
        List<int> items = new List<int>();

        items = Enumerable.Range(0, 1000000).ToList();

        Reverse(items);
        CopyWithToList(items);
        CopyWithForeach(items);
    }

    public static void Reverse<T>(List<T> list) 
    {
        var sw = Stopwatch.StartNew();
        list.Reverse();
        sw.Stop();
        Console.WriteLine("ReversedList:    {0}", sw.Elapsed);
    }

    public static void CopyWithToList<T>(List<T> list) 
    {
        var sw = Stopwatch.StartNew();
        List<T> copy = list.ToList();
        sw.Stop();
        Console.WriteLine("CopyWithToList:  {0}", sw.Elapsed);
    }

    public static void CopyWithForeach<T>(List<T> list)
    {
        var sw = Stopwatch.StartNew();
        List<T> copy = new List<T>();
        foreach (T item in list) {
            copy.Add(item);
        }
        sw.Stop();

        Console.WriteLine("CopyWithForeach: {0}", sw.Elapsed);
    }

}