使用 foreach 循环连接两个 List<T>

3

我试图稍微离开LINQ,虽然它总体上非常有用,但有时阅读起来也很困难。

以前我使用LINQ执行连接(完全外连接),但更喜欢使用for / foreach循环进行连接,因为它们更简单。我刚刚将一个LINQ语句(不是PLINQ)转换为嵌套的foreach循环,性能严重下降。以前需要几秒钟,现在需要约一分钟,代码如下所示。

foreach (var p in PortfolioELT)
{
    double meanloss;
    double expvalue;
    double stddevc;
    double stddevi;
    bool matched = false;

    foreach (var a in AccountELT)
    {
        if (a.eventid == p.eventid)
        { DO SOME MATH HERE <-----

有没有关于以下问题的想法:

  1. 为什么这比LINQ Join慢?
  2. 如何加速它?

程序显然达到了它需要达到的目标,但速度太慢。

编辑: 旧代码全文:

public static ConcurrentList<Event> CreateNewELTSUB(IList<Event> AccountELT, IList<Event> PortfolioELT)
    {
        if (AccountELT == null)
        {
            return (ConcurrentList<Event>)PortfolioELT;
        }
        else
        {
            //Subtract the Account ELT from the Portfolio ELT
            var newELT = from p in PortfolioELT
                         join a in AccountELT
                         on p.eventid equals a.eventid into g
                         from e in g.DefaultIfEmpty()
                         select new
                         {
                             EventID = p.eventid,
                             Rate = p.rate,
                             meanloss = p.meanloss - (e == null ? 0d : e.meanloss),
                             expValue = p.expValue - (e == null ? 0d : e.expValue),
                             stddevc = Math.Sqrt(Math.Pow(p.stddevc, 2) - (e == null ? 0d : Math.Pow(e.stddevc, 2))),
                             stddevi = Math.Sqrt(Math.Pow(p.stddevi, 2) - (e == null ? 0d : Math.Pow(e.stddevi, 2)))
                         };


            ConcurrentList<Event> list = new ConcurrentList<Event>();
            foreach (var x in newELT)
            {
                list.Add(new Event(x.meanloss, x.EventID, x.expValue, x.Rate, x.stddevc, x.stddevi));
            }
            return list;
        }
    }

新代码完整:

    public static ConcurrentList<Event> CreateNewELTSUB(IList<Event> AccountELT, IList<Event> PortfolioELT)
    {
        if (AccountELT == null)
        {
            return (ConcurrentList<Event>)PortfolioELT;
        }
        else
        {
            //Subtract the Account ELT from the Portfolio ELT
            ConcurrentList<Event> newlist = new ConcurrentList<Event>();

            //Outer Join on Portfolio ELT
            foreach (var p in PortfolioELT)
            {
                double meanloss;
                double expvalue;
                double stddevc;
                double stddevi;
                bool matched = false;

                foreach (var a in AccountELT)
                {
                    if (a.eventid == p.eventid)
                    {
                        matched = true;
                        meanloss = p.meanloss - a.meanloss;
                        expvalue = p.expValue - a.expValue;
                        stddevc = Math.Sqrt((Math.Pow(p.stddevc, 2)) - (Math.Pow(a.stddevc, 2)));
                        stddevi = Math.Sqrt((Math.Pow(p.stddevi, 2)) - (Math.Pow(a.stddevi, 2)));
                        newlist.Add(new Event(meanloss, p.eventid, expvalue, p.rate, stddevc, stddevi));
                    }
                    else if (a.eventid != p.eventid)    //Outer Join on Account
                    {
                        newlist.Add(a);
                    }
                }
                if (!matched)
                {
                    newlist.Add(p);
                }
            }
            return newlist;
        }

3
请提供完整的概念示例... - Tigran
你能举个例子说明你的LINQ很难读吗?我发现,当LINQ被正确地结构化时,它的可读性是最大的优势之一。你只使用了方法语法吗? - Kittoes0124
2
这种写法比LINQ更易读吗? - Eren Ersönmez
3
LINQ 的等价物是什么? - manojlds
你能发布一个可用的版本吗?如果没有看到LINQ版本和foreach版本,我们无法帮助发现哪里出现了缓慢。 - Chuck Conway
显示剩余2条评论
2个回答

6

为什么这比LINQ Join慢?

我有意跳过回答。

如何提高速度?

你现在是针对每个PortfolioELT遍历整个AccountELT集合。你应该只遍历一个集合,把另一个转换成字典使得查找指定记录更容易。类似这样:

var accountELTIdx = AccountELT.ToDictionary(k => k.eventid);

那么

foreach (var p in PortfolioELT)
{
    double meanloss;
    double expvalue;
    double stddevc;
    double stddevi;
    bool matched = false;

    if(accountELTIdx.ContainsKey(p.eventid)
    {
        var acct = accountELTIdx[p.eventid];
        // some maths
    }
    ....

优秀而简洁的回答。谢谢您,先生。 - Richard Todd

2
您每次循环都会创建本地变量,这些变量可能不会被使用。
            double meanloss;
            double expvalue;
            double stddevc;
            double stddevi;
            bool matched = false;

您正在进行线性搜索以匹配事件ID,如果其中一个列表按“eventid”排序,则可以使用二进制搜索而不是完整的线性搜索来避免浪费。

            foreach (var a in AccountELT)
            {
                if (a.eventid == p.eventid)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接