【问题标题】:Comparing two lists and adding missing items - improvement of performance比较两个列表并添加缺失项 - 提高性能
【发布时间】:2017-02-03 14:11:49
【问题描述】:

我编写了一个基本上比较两个列表的代码,并检查它们名为 TransactionID 的属性,如果 TransactionID 不存在于旧列表中,它将被添加到仅包含旧项目的新列表中。

所以列表是:

// list named: prepared - contains all old and new items 

// UserTransactions from DB  - list - contains only old transactions

现在我比较这两个列表,看看准备的列表是否有一些项目不存在于 db 的 usertransaction 列表中,如下所示:

var ListDoAdd = prepared.Where((i) => ctx.EbayUserTransactions.Where(x => x.SearchedUserID == updatedUser.SearchedUserID).ToList().FindIndex((el) => el.TransactionID == i.TransactionID) == -1).ToList();

现在 ListDoAdd 最终包含了 DB 中缺少的所有项目。

当我处理大量记录时,这种方式效率非常低。

我认为我可以做的是首先将特定用户的所有事务加载到内存中,如下所示:

var oldList = ctx.UserTransactions.Where(x => x.SearchedUserID == updatedUser.SearchedUserID).ToList()

然后在运行时比较这两个列表以加快速度,而不是逐个检查每个项目,我上面显示的这个方法现在没有?

So now  I have:

prepared list

and 

oldList 

现在我只需要找出最快的方法来比较两个列表并找到丢失的项目...

有人可以帮我解决这个问题吗?

附:伙计们,如果我要做多线程,那么唯一可能的方法就是使用 PLINQ,不是吗?

【问题讨论】:

  • 有人吗???
  • 我希望我没有遗漏什么,但听起来您只需要遍历可能有新项目的列表并检查该项目是否在另一个列表中。如果没有,则添加它。单次通过此列表即可完成此操作。
  • @JohnG 是的,就是这样,我只想写尽可能快的方法,以便在列表更大时减少等待时间,比如比较两个列表中的 10-15k 个项目......这会耗费大量时间

标签: c# list c#-4.0 parallel-processing compare


【解决方案1】:

如果两个列表都加载到内存中,我建议使用带有自定义比较器的 set 方法,如下所示:

public class UserTransactionByIdComaprer : IEquialityComparer<UserTransaction>
{
     pulic static readonly IEqualityComparer<UserTransaction> Instance = new UserTransactionByIdComaprer();

     public bool Equals(UserTransaction x, UserTransaction y)
     {
         return x.TransactionId == y.TransactionId;
     }

     public int GetHashCode(UserTransaction x)
     {
          return x.TransactionId.GetHashCode();
     }

}

var prepared = ....
var old = ...

var diff = prepared.Except(old, UserTransactionByIdComaprer.Instance); // this are all that are not present in the old list

使用 set 函数会给你带来更好的性能,主要是因为它只会枚举集合一次。更多信息:Set Operations

关于并行:您可以非常轻松地并行化查询。

var diff = prepared.Except(old, UserTransactionByIdComaprer.Instance)
                   .AsParallel()
                   .WithDegreeOfParallelism(2)

我会建议你做一些性能测试。对于包含 15k 个元素的集合,我可以在没有并行内容的情况下为您提供一些性能提升。

注意事项:如果并行和非并行版本具有相似的时序,并且您在高负载的服务器上运行它,其中每个线程可能很重要,我建议使用非并行版本.

【讨论】:

  • 嘿瓦西尔,非常感谢。如果两个列表都包含 15000 个项目,这会很快工作吗?
  • 是的,但我确信非并行版本的 15k 个元素将在 30 毫秒内运行,当然取决于您使用的机器,因此并行版本有些无关紧要;]
  • 刚刚测试了 15k 个整数,另外 15k 个整数除外。非并行版本运行 6 毫秒,并行(2 核)运行 18 毫秒,因此较小的集合会产生开销。
  • 使用非并行,我看到具有 750k 元素的集合有一些性能优势,当然这因机器而异,等等,但你可以自己测试它;]
  • 你现在可以检查一下吗,我已经编辑了一些代码,我猜你不经常使用 c# 吗?我使用了 c# 6 中引入的 c# 语法和一些伪代码,所以这可能是问题所在,检查是否正常,如果不能分享一些细节,我可能会有所帮助
【解决方案2】:

如果我很好理解,你想比较两个列表。 为此,通常我们通过 LINQ Left Join 来完成。 请看下面的代码:

from p in context.ParentTable 
join c in context.ChildTable on p.ParentId equals c.ChildParentId into j1
from j2 in j1.DefaultIfEmpty()group j2 by p.ParentId into grouped
select new { ParentId = grouped.Key, Count = grouped.Count() }

希望对你有用

【讨论】:

  • 一段不错的代码,但你可以看看我写了一个更简单的版本。 LINQ 非常好和有效地处理了这个问题
【解决方案3】:

如果您有两组包含名为TransactionID 的属性的类型,并且您想查找第二组中缺少的一组元素,则可以使用Enumerable.Except()

在您可以使用Enumerable.Except() 之前,您需要一个IEqualityComparer&lt;Transaction&gt; 的实现,因为它用于比较集合中的项目。

假设您的事务类如下所示:

class Transaction
{
    public int TransactionID;
}

那么您对IEqualityComparer&lt;Transaction&gt; 的实现将是:

class Comparer : IEqualityComparer<Transaction>
{
    public bool Equals(Transaction x, Transaction y)
    {
        return x.TransactionID == y.TransactionID;
    }

    public int GetHashCode(Transaction obj)
    {
        return obj.TransactionID.GetHashCode();
    }
}

鉴于此,您可以像这样找到丢失的项目:

var missing = oldList.Except(newList, new Comparer());

例如:

static void Main()
{
    var oldList = new List<Transaction>
    {
        new Transaction{ TransactionID = 1 },
        new Transaction{ TransactionID = 2 },
        new Transaction{ TransactionID = 3 },
        new Transaction{ TransactionID = 4 },
        new Transaction{ TransactionID = 5 },
    };

    var newList = new List<Transaction>
    {
        new Transaction{ TransactionID = 2 },
        new Transaction{ TransactionID = 4 },
    };

    var missing = oldList.Except(newList, new Comparer());

    foreach (var item in missing) // This prints "1", "3" and "5".
    {
        Console.WriteLine(item.TransactionID);
    }
}

[编辑] 这是完整的可编译应用程序。

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

namespace ConsoleApplication1
{
    class Transaction
    {
        public int TransactionID;
    }

    class Comparer : IEqualityComparer<Transaction>
    {
        public bool Equals(Transaction x, Transaction y)
        {
            return x.TransactionID == y.TransactionID;
        }

        public int GetHashCode(Transaction obj)
        {
            return obj.TransactionID.GetHashCode();
        }
    }

    class Program
    {
        static void Main()
        {
            var oldList = createList(0, 1, 50000000);
            var newList = createList(0, 2, 50000000/2);
            var comparer = new Comparer();

            Stopwatch sw = new Stopwatch();

            for (int i = 0; i < 4; ++i)
            {
                sw.Restart();
                var missing = oldList.Except(newList, comparer);
                Console.WriteLine(missing.Count());
                Console.WriteLine("Linq: " + sw.Elapsed);

                sw.Restart();
                missing = oldList.Except(newList, comparer).AsParallel();
                Console.WriteLine(missing.Count());
                Console.WriteLine("Plinq: " + sw.Elapsed);
            }
        }

        static List<Transaction> createList(int startingId, int idIncrement, int count)
        {
            var result = new List<Transaction>(count);

            for (int i = 0; i < count; ++i, startingId += idIncrement)
                result.Add(new Transaction {TransactionID = startingId});

            return result;
        }
    }
}

【讨论】:

  • 这个解决方案会比执行两个并行循环并比较两个列表更有效吗?
  • 我问这个是因为我不是在比较两个列表中的 5-10 个项目,而是比较两个列表中的 15k 与 15k 个项目 =)
  • @User987 这可能会更有效,因为Except() 的复杂度为O(N) 而不是O(N^2)。您应该始终执行时序测试以确保。
  • 从技术上讲这不应该颠倒过来吗?如果新交易包含旧列表中已经存在的项目,缺失列表的大小应该是 0,而不是 1,3 和 5?
  • 马修,所以如果旧列表是这样的:1,2,3,4,5,如果新列表是:1,2,3,4,5,6 => 缺失将是 6缺失项目的集合大小将是 1 ,不是?
猜你喜欢
  • 1970-01-01
  • 2017-08-28
  • 1970-01-01
  • 1970-01-01
  • 2020-08-08
  • 2023-04-04
  • 2019-08-04
  • 2021-12-23
  • 1970-01-01
相关资源
最近更新 更多