【问题标题】:Finding the difference between two lists of strings查找两个字符串列表之间的差异
【发布时间】:2014-08-29 08:55:52
【问题描述】:

我很确定这是重复的,但我已经尝试了所有方法,但我似乎仍然无法获得差异。我有两个字符串列表:listA 和 listB。我正在尝试在 listA 中查找不在 B 中的项目。

示例: 列表A:“1”、“2”、“4”、“7” 列表B:“2”,“4” 我想要的输出是:“1”,“7”

这是我尝试过的 for 循环和 lambda 表达式,但这些都需要很长时间:

//these two approaches take too long for huge lists

    foreach (var item in listA)
            {
                if (!listB.Contains(item))
                    diff.Add(id);
            }

    diff = listA.Where(id => !listB.Contains(id)).ToList();

//these don't give me the right differences

    listA.Except(listB).ToList();

    var set = new HashSet<string>(listA);
    set.SymmetricExceptWith(listB);

【问题讨论】:

  • 你想如何管理同一个列表中的重复项? (想象 listA 或 listB 有 1、1、1、2、4、7、4、3)?
  • 你能假设列表是排序的吗?它们是一组还是有碰撞?另外:如果一个列表足够大,您可以对此类列表进行分区以并行计算。
  • 它们没有排序,重复的可以丢弃。只需要明显的差异。不过不应该有任何重复——我们可以在我的情况下做出这样的假设。
  • var diff=listA.Except(listB).ToList();
  • listA.Except(listB).ToList() 按预期工作。重现问题的邮政编码。

标签: c# .net string linq lambda


【解决方案1】:

使用 LINQ 的 Except 方法:

listA.Except(listB).ToList();

【讨论】:

    【解决方案2】:
    listA.Except(listB).ToList();
    

    应该给出正确答案,但是

    set.SymmetricExceptWith(listB);
    

    不应该。 SymmetricExcept 将给出 listA 中不在 listB 中的项目加上 ListB 中不在 ListA 中的项目。

    【讨论】:

      【解决方案3】:

      你发布的所有代码都应该可以正常工作,所以无论如何你写的错误都在另一个地方“这些需要很长时间”那么我想你有一个性能问题。

      让我们做一个非常快速和肮脏的比较(你知道做一个好的性能测试是一个漫长的过程,自我推销:基准已经完成this free tool)。假设:

      • 列表是无序的。
      • 我们的输入中可能有重复,但我们不希望结果中出现重复。
      • 第二个列表始终是第一个列表的子集(假设是因为您使用的是SymmetricExceptWith,如果不是,那么它的结果与Except 相比有很大不同)。如果这是一个错误,请忽略对 SymmetricExceptWith 的测试。

      20,000 个随机项目的两个列表(测试重复 100 次然后取平均值,发布模式)。

      方法时间 [ms] 包含 *1 49.4 包含 *2 49.0 5.9除外 SymmetricExceptWith *3 4.1 SymmetricExceptWith *4 2.5

      注意事项:

      1 使用 foreach 循环
      2 使用 for
      3 测量哈希集创建
      4 未测量哈希集创建。我将其包含在内以供参考,但如果您没有第一个列表作为 Hashset,则不能忽略创建时间。

      我们看到Contains() 方法非常慢,因此我们可以在更大的测试中放弃它(无论如何我检查过,它的性能不会变得更好甚至可比)。让我们看看 1,000,000 个项目列表会发生什么。

      方法时间 [ms] 244.4 除外 SymmetricExceptWith 259.0

      让我们尝试使其并行(请注意,对于本次测试,我使用的是旧的 Core 2 Duo 2 GHz):

      方法时间 [ms] 244.4 除外 SymmetricExceptWith 259.0 除了(并行分区)301.8 SymmetricExceptWith (p. p.) 382.6 除了(AsParallel)274.4

      并行性能更差,LINQ except 是现在最好的选择。让我们看看它在更好的 CPU(Xeon 2.8 GHz,四核)上是如何工作的。另请注意,如此大的数据缓存大小不会对测试产生太大影响。

      方法时间 [ms] 127.4 除外 SymmetricExceptWith 149.2 除了(并行分区)208.0 SymmetricExceptWith (p. p.) 170.0 (AsParallel)80.2 除外

      总结:对于相对较小的列表SymmetricExceptWith() 会表现更好,对于大列表Except() 总是更好。如果您的目标是现代多核 CPU,那么并行实现的扩展性会更好。在代码中:

      var c = a.Except(b).ToList();
      var c = a.AsParallel().Except(b.AsParallel()).ToList();
      

      请注意,如果您不需要List&lt;string&gt; 作为结果并且IEnumerable&lt;string&gt; 就足够了,那么性能将大大提高(并且与并行执行的差异会更大)。

      当然,这两行代码并不是最优的,可以大大增加(如果它真的对性能非常关键,您可以选择 ParallelEnumerable.Except() 实现作为您自己的特定高度优化例程的起点)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-08
        • 2015-03-19
        • 2016-07-04
        • 1970-01-01
        相关资源
        最近更新 更多