C# - 查找重复行的列表(需要优化)

本文关键字:优化 列表 查找 | 更新日期: 2023-09-27 18:35:15

如果可能的话,我想在 C# 中优化这段代码。

当少于 1000 行时,没问题。但是当我们至少有 10000 个时,它开始需要一些时间......这里有一个小基准:

  • 5000 行 => ~2s
  • 15000 行 => ~20s
  • 25000 行 => ~50s

确实,我正在寻找重复的行。

方法序列等于检查值可能是一个问题(在我的"基准测试"中,我有 4 个字段被视为"键字段"......

这是代码:

private List<DataRow> GetDuplicateKeys(DataTable table, List<string> keyFields)
{
    Dictionary<List<object>, int> keys = new Dictionary<List<object>, int>(); // List of key values + their index in table
    List<List<object>> duplicatedKeys = new List<List<object>>(); // List of duplicated keys values 
    List<DataRow> duplicatedRows = new List<DataRow>(); // Rows that are duplicated
    foreach (DataRow row in table.Rows)
    {
        // Find keys fields values for the row
        List<object> rowKeys = new List<object>();
        keyFields.ForEach(keyField => rowKeys.Add(row[keyField]));
        // Check if those keys are already defined
        bool alreadyDefined = false;
        foreach (List<object> keyValue in keys.Keys)
        {
            if (rowKeys.SequenceEqual(keyValue))
            {
                alreadyDefined = true;
                break;
            }
        }
        if (alreadyDefined)
        {
            duplicatedRows.Add(row);
            // If first duplicate for this key, add the first occurence of this key
            if (!duplicatedKeys.Contains(rowKeys))
            {
                duplicatedKeys.Add(rowKeys);
                int i = keys[keys.Keys.First(key => key.SequenceEqual(rowKeys))];
                duplicatedRows.Add(table.Rows[i]);
            }
        }
        else
        {
            keys.Add(rowKeys, table.Rows.IndexOf(row));
        }
    }
    return duplicatedRows;
}

有什么想法吗?

C# - 查找重复行的列表(需要优化)

我认为这是查找重复行的最快和最短的方法:

对于 100.000 行,它在大约 250 毫秒内执行。

Main和测试数据:

static void Main(string[] args)
{
    var dt = new DataTable();
    dt.Columns.Add("Id");
    dt.Columns.Add("Value1");
    dt.Columns.Add("Value2");
    var rnd = new Random(DateTime.Now.Millisecond);
    for (int i = 0; i < 100000; i++)
    {
        var dr = dt.NewRow();
        dr[0] = rnd.Next(1, 1000);
        dr[1] = rnd.Next(1, 1000);
        dr[2] = rnd.Next(1, 1000);
        dt.Rows.Add(dr);
    }
    Stopwatch sw = new Stopwatch();
    sw.Start();
    var duplicates = GetDuplicateRows(dt, "Id", "Value1", "Value2");
    sw.Stop();
    Console.WriteLine(
        "Found {0} duplicates in {1} miliseconds.", 
        duplicates.Count,
        sw.ElapsedMilliseconds);        
    Console.ReadKey();
}

GetDuplicateRows LINQ

private static List<DataRow> GetDuplicateRows(DataTable table, params string[] keys)
{
    var duplicates =
        table
        .AsEnumerable()
        .GroupBy(dr => String.Join("-", keys.Select(k => dr[k])), (groupKey, groupRows) => new { Key = groupKey, Rows = groupRows })
        .Where(g => g.Rows.Count() > 1)
        .SelectMany(g => g.Rows)
        .ToList();
    return duplicates;
}

解释(对于那些刚接触LINQ的人):

我猜最棘手的部分是GroupBy。在这里,我将DataRow作为第一个参数,对于每一行,我根据我连接的指定键的值创建一个组键,以创建一个类似 1-1-2 的字符串。然后,第二个参数仅选择组键,并将组行放入新的匿名对象中。然后我检查是否有超过 1 行并将组展平为带有 SelectMany 的列表。

试试这个。使用更多的 linq,以提高性能,如果可能的话,也可以尝试使用 PLinq。

问候

private List<DataRow> GetDuplicateKeys(DataTable table, List<string> keyFields)
{
    Dictionary<List<object>, int> keys = new Dictionary<List<object>, int>(); // List of key values + their index in table
    List<List<object>> duplicatedKeys = new List<List<object>>(); // List of duplicated keys values 
    List<DataRow> duplicatedRows = new List<DataRow>(); // Rows that are duplicated
    foreach (DataRow row in table.Rows)
    {
        // Find keys fields values for the row
        List<object> rowKeys = new List<object>();
        keyFields.ForEach(keyField => rowKeys.Add(row[keyField]));
        // Check if those keys are already defined
        bool alreadyDefined = false;
        foreach (List<object> keyValue in keys.Keys)
        {
            if (rowKeys.Any(keyValue))
            {
                alreadyDefined = true;
                break;
            }
        }
        if (alreadyDefined)
        {
            duplicatedRows.Add(row);
            // If first duplicate for this key, add the first occurence of this key
            if (!duplicatedKeys.Contains(rowKeys))
            {
                duplicatedKeys.Add(rowKeys);
                int i = keys[keys.Keys.First(key => key.SequenceEqual(rowKeys))];
                duplicatedRows.Add(table.Rows[i]);
            }
        }
        else
        {
            keys.Add(rowKeys, table.Rows.IndexOf(row));
        }
    }
    return duplicatedRows;
}