如何比较和更新包含大量项目的2个列表?

时间:2019-06-22 15:23:53

标签: c# list linq

注意:使用LINQ连接解决了这个问题。

如果目标列表中存在列表值,我需要比较“源列表”中的列表值,如果是,则将其保存到第三个列表中。

我编写的代码确实起作用,但是由于“源列表”中有3万个项目,因此花了很多时间,并且将每个项目的值与目标列表中的1500万个目标值进行比较,这花费了很多时间。因为它每次都会遍历整个列表(30k * 1500万次)

请参阅代码,该代码显然不是最佳方法,但是可以完成工作。

        // The below code will generate the lists from CSV file
        The lists are below for sample

        **Source List**
        FileId  FilePath      FileChecksum
        1       somepath A    check1
        2       somepath AA   check2
        3       somepath AAB  check3
        4       somepath B    check4
        5       somepath BB   check5

        **Destination List**

        StepId  StatusID  JobId ProjectId FileId     FilePath
        5        6         4    2091      577206853  somepath A
        5        6         4    2092      577206853  somepath AA
        5        6         4    2093      577206853  somepath AAA
        5        6         4    2094      577206853  somepath AB
        5        6         4    2095      577206853  somepath A
        5        6         4    2096      577206853  somepath B
        5        6         4    2097      577206853  somepath BB

        List<Source> SourceList = File.ReadAllLines(@"D:\source.csv").Skip(1).Select(v => Source.SourceFromCSv(v)).ToList();

        List<Destination> DestinationList = File.ReadAllLines(@"D:\Destination.csv").Skip(1).Select(d => Destination.FromDestinationCSV(d)).ToList();

        //This will compare and create a new list
        var result1 =
            from s in SourceList
            from d in DestinationList
            where (d.FilePath.ToLower() == s.FilePath.ToLower())
             select (d.StepId + "," + d.StatusId + "," + d.JobId + "," + 
             d.ProjectId + "," + d.FileId + "," + d.FilePath + "," + 
             s.FileChecksum);



             Expected Result:
             StepId StatusID  JobId ProjectId FileId    FilePath      FileChecksum
             5       6         4    2091      577206853 somepath A    check1
             5       6         4    2092      577206853 somepath AA   check2
             5       6         4    2095      577206853 somepath A    check1
             5       6         4    2096      577206853 somepath B    check4
             5       6         4    2097      577206853 somepath BB   check5

5 个答案:

答案 0 :(得分:3)

您可以对两个列表进行排序,然后逐行进行比较。 算法复杂度为O(n log n + n)。

将数据A的第一行与数据B的第一行进行比较,然后增加“更大”行上指针的索引。 如果数据A具有8,数据B具有7和9,则一旦达到9,您就会知道数据B中不存在8。

您应该以最大可能的索引开始比较。这样,如果列表确实是子列表,则可以快速终止。

答案 1 :(得分:1)

您可以反过来做。无需选择3万个源条目中的一个,就可以遍历3000万个条目。然后,如果您找到了所有3万条条目,或者在最坏的情况下,是在找到3000万条条目之后,就可以停止。那仍然比30K * 15M好。

答案 2 :(得分:1)

是的,如果您不需要列表的所有功能,将基本类型设置为HashSet<T>会大大改善查找。您的自定义类型可能需要实现适当的GetHashCode()函数,以进一步提高查找速度。

请参阅:

不要调用new HashSet(query.ToList()),而是在实例化列表query.ToHashSet()时直接转换为哈希集,可以选择传入Equal比较器,如下所示:

除了自定义GetHashCode的实现之外,您还可以实现自定义IEqualityComparer来处理诸如特定字段构成平等规则的特定情况,例如您的情况。如今,Visual Studio和Resharper提供了built-in refactor to generate a good implementation of GetHashCode and Equals

请参阅:

然后您可以使用IntersectWith在一次调用中获取两组中的所有项目:

请参阅:

创建一个特殊的对象,您可以将SourceDestination都转换为对象,或者给它们提供相同的基类将允许这样做。

您也可以使用IDictionary<Key, Value>并将密钥设为Item.FilePath.ToLower(),与上述相同。这将允许运行时使用字符串的GetHashCode检查项目是否存在于其他列表中,该字符串在默认情况下进行了高度优化。

答案 3 :(得分:1)

var query = from s in SourceList
 join d in DestinationList on 
 s.FilePath.ToLower().TrimEnd() equals d.FilePath.ToLower().TrimEnd()
 select (d.StepId + "," + d.StatusId + "," + d.JobId + "," +d.ProjectId + "," + d.FileId + "," + d.FilePath + "," + s.FileChecksum);

LINQ join在不到5秒的时间内完成了同一件事。

答案 4 :(得分:0)

原则上您要做的就是将文件校验和附加到目标列表的末尾。

在源列表之外创建哈希或字典,然后新列表如下所示。

//create dictionary SourceDictionary<string,string> with key = filepath.tolower and value = checksum
var newList = DestinationList.select(d => $"{d.thing1},{d.thingN}" + SourceDictionary[d.filename.tolower()])

应该更快