0

我有三个 System.Array,其中基本上存储了 10 个字符的电话号码。定义是这样的:

private static System.Array objRowAValues;
private static System.Array objRowBValues;
private static System.Array objRowCValues;

我这样做是因为我读取了一个包含许多单元格(大约 1 000 000 个)的巨大 Excel 文件,并且处理List<string>速度有点慢。稍后在我的代码中,我将 System.Array 更改为 aList<string>主要是因为我填充了一个 ListBox 元素。这些是列表的定义

private List<string> bd = new List<string>();
private List<string> bl = new List<string>();
private List<string> cm = new List<string>();

我想检查 objRowAValues 中的任何值是否存在于 objRowBValues 或 objRowCValues 中,如果存在则删除现有值,我该怎么做?我是 C# 的新手,这是我的第一步。

编辑:这是我正在做的代码(仅相关部分):

private List<string> bd = new List<string>();
private static System.Array objRowAValues;

private List<string> bl = new List<string>();
private static System.Array objRowBValues;

private List<string> cm = new List<string>();
private static System.Array objRowCValues;

private List<string> pl = new List<string>(); 

private static Microsoft.Office.Interop.Excel.Application appExcel;

Excel.Application xlApp;
Excel.Workbook xlWorkBook;
Excel.Worksheet xlWorkSheet;
Excel.Range rngARowLast, rngBRowLast, rngCRowLast;

long lastACell, lastBCell, lastCCell, fullRow;

// this is the main method I use to load all three Excel files and fill System.Array and then convert to List<string>

private void btnCargarExcel_Click(object sender, EventArgs e)
    {
        if (this.openFileDialog1.ShowDialog() == DialogResult.OK)
        {
            if (System.IO.File.Exists(openFileDialog1.FileName))
            {
                Stopwatch stopWatch = new Stopwatch();
                stopWatch.Start();
                Thread.Sleep(10000);

                filePath.Text = openFileDialog1.FileName.ToString();

                xlApp = new Microsoft.Office.Interop.Excel.Application();
                xlWorkBook = xlApp.Workbooks.Open(openFileDialog1.FileName, 0, true, 5, "", "", true,
                                                  Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false,
                                                  false, 0, true, 1, 0);
                xlWorkSheet = (Excel.Worksheet) xlWorkBook.Worksheets.get_Item(1);

                fullRow = xlWorkSheet.Rows.Count;
                lastACell = xlWorkSheet.Cells[fullRow, 1].End(Excel.XlDirection.xlUp).Row;
                rngARowLast = xlWorkSheet.get_Range("A1", "A" + lastACell);
                objRowAValues = (System.Array) rngARowLast.Cells.Value;

                foreach (object elem in objRowAValues) { bd.Add(cleanString(elem.ToString(), 10)); }

                nrosProcesados.Text = bd.Count().ToString();
                listBox1.DataSource = bd;

                xlWorkBook.Close(true, null, null);
                xlApp.Quit();

                releaseObject(xlWorkSheet);
                releaseObject(xlWorkBook);
                releaseObject(xlApp);

                stopWatch.Stop();

                TimeSpan ts = stopWatch.Elapsed;
                executiontime.Text =
                    String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds,
                                  ts.Milliseconds/10).ToString();
            }
            else
            {
                MessageBox.Show("No se pudo abrir el fichero!");
                System.Runtime.InteropServices.Marshal.ReleaseComObject(appExcel);
                appExcel = null;
                System.Windows.Forms.Application.Exit();
            }
        }
    }

    // This is the method where I clean the existent values from bd (the List where I should remove existent values on bl and existent values on cm
   private void btnClean_Click(object sender, EventArgs e)
    {
        pl = bd;
        Stopwatch stopWatch = new Stopwatch();
        stopWatch.Start();
        Thread.Sleep(10000);

        pl.RemoveAll(element => bl.Contains((element)));
        pl.RemoveAll(element => cm.Contains((element)));

        textBox2.Text = pl.Count.ToString();
        listBox4.DataSource = pl;

        stopWatch.Stop();

        TimeSpan ts = stopWatch.Elapsed;
        textBox6.Text =
            String.Format("{0:00}:{1:00}:{2:00}.{3:00}", ts.Hours, ts.Minutes, ts.Seconds,
                          ts.Milliseconds / 10).ToString();

    }
4

4 回答 4

1

实际上有两个问题:

  1. 如何以性能有效的方式找到(巨大的)集合中存在的值?
  2. 如何从正在枚举的集合中删除值?

对于第一个,我建议使用HashSet, 因为Contains它的操作需要 O(1) 而不是 O(n) 的List/ Array

第二个有点棘手 - 您可以遍历其余部分,而不是迭代第一个集合,然后尝试从第一个集合中删除这些项目。

在 的情况下HashsetRemove包括Contains逻辑和返回bool以指示值是否在集合中,因此它也更容易HashSet用于第一个集合。

所以它应该看起来像这样:

    var a = new HashSet<string>();

    // OR, if you have existing array:
    // var a = new HashSet<string>(myArray);

    var b = new HashSet<string>();
    var c = new HashSet<string>();

    // ... some filling logic here ...

    foreach (var item in b)
    {
        a.Remove(item);
    }

    foreach (var item in c)
    {
        a.Remove(item);
    }

之后,您可以使用ToList()将值绑定到您的控件 - 它将创建具有所有未删除值的新数组:

    List<string> aList = a.ToList();
于 2013-04-14T18:49:23.737 回答
0

在对 OP 的评论的基础上,您需要执行以下操作:

...make your lists...
bd.removeAll(phoneNum => bl.contains(phoneNum) || cm.contains(phoneNum));

请注意,您应该使用arrays. 您应该改为使用List<string>上述所有三个。

于 2013-04-14T18:46:00.833 回答
0

假设它们是 List 并且您只想丢弃 中的元素bd,因为您没有明确说明应该从哪些列表中删除,我会这样做:

bd.RemoveAll(element => bl.Contains((element)));
bd.RemoveAll(element => cm.Contains((element)));

或者你可以尝试使用HashSet

private List<string> hsA = new List<string>();
private List<string> hsB = new List<string>();
private List<string> hsC = new List<string>();

并尝试使用相同的方法:

hsA.RemoveWhere(element => hsB.Contains(element) || hsC.Contains(element));
于 2013-04-14T18:48:53.603 回答
0

首先,如果您知道有多少行,请尝试使用适当的最大大小初始化 List。所以它不必不断地调整底层数组的大小。或者使用类型保存数组string[]来为您节省一些转换等的麻烦。

如果要过滤 RowAValues 列表,可以使用 Linq 进行:

rowAValues = rowAValues.Where(x => !rowBValues.Contains(x) 
                                && !rowCValues.Contains(x))
                       .ToArray()

如果您当然使用列表,则将 ToArray 替换为 ToList。

请记住,迭代数千个项目是相当性能密集型的。如果检查多次执行,请考虑HashSet<string>对行 b+c 使用 a。代码保持不变,但查找速度会很快。

LINQ(语言集成查询)的一个很好的参考是http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b

于 2013-04-14T18:49:11.377 回答