基于比较c#将元素添加到列表时使用的最佳数据结构是什么?

时间:2013-09-17 18:46:51

标签: c# list data-structures hashset sortedset

  List<string> allApps = new List<string>();
        roster = MURLEngine.GetUserFriendDetails(token, userId);
        var usersfriends = from elements in roster.RosterEntries
                           where elements[0] == 'm' && elements[1] >= '0' && elements[1] <= '9'
                           select elements;
        foreach (string userid in usersfriends)
        {
            roster = MURLEngine.GetUserFriendDetails(token, userid);
            var usersapps = from elements in roster.RosterEntries
                            where elements[0] != 'm'
                            select elements;
            allApps.AddRange(usersapps);

            allApps = allApps.Distinct().ToList();
        }



        int countapps = 0;
        List<string> Appname = new List<string>();
        countapps = appList.Count();

        for (int y = 0; y < countapps; y++)
        {
            foreach (string li in allApps)  // 
            {
                bool istrueapp = appList.ElementAt(y).AppName.Equals(li);
                if (istrueapp == true)
                {
                    Appname.Add(appList.ElementAt(y).AppName);
                }
            }
        }

在上面的代码中,我首先得到一个字符串列表,即usersfriends然后基于那些id我将获得用户的应用程序列表,然后将所有用户的所有应用程序添加到另一个列表,即allApps因此整个过程使用列表执行此操作需要大约20秒。尝试使用HashSet和SortedSet,但它更慢。

我的问题是我应该在这个场景中使用什么数据结构?

真的会帮助我

4 个答案:

答案 0 :(得分:3)

我最喜欢LINQ的是它让你描述你想做什么,而不是让你写出一堆模糊你的目标的循环。这是你的代码的重构版本,我认为非常清楚,并且在我的测试平台上运行得更快(0.5s vs~15s)。

// create a hashset for fast confirmation of app names
var realAppNames = new HashSet<string>(appList.Select(a => a.AppName));

var roster = MURLEngine.GetUserFriendDetails(token, userId);

// get the ids of all friends
var friendIds = roster.RosterEntries
                      .Where (e => e[0] == 'm' && e[1] >= '0' && e[1] <= '9');

var apps = 
    friendIds 
    // get the rosters for all friends
    .SelectMany (friendId => MURLEngine.GetUserFriendDetails(token, friendId)).RosterEntries)
    // include the original user's roster so we get their apps too
    .Concat(roster.RosterEntries) 
    // filter down to apps only
    .Where (name => name[0] != 'm' && realAppNames.Contains(name)) 
    // remove duplicates
    .Distinct()
    // we're done!
    .ToList();

答案 1 :(得分:1)

好的,到目前为止我可以提出什么建议。

首先:你有很多Add的 一般来说,默认List<T>不是Add批量的最佳数据结构,因为它在内部实现为数组,当数据已满时会被销毁并复制到较大的数组中。 有两种选择:
  - 创建具有预定义容量的列表:List<string> allApps = new List<string>(countOfApps);。如果您可以提前大致计算要添加到列表中的项目数,那么这个很好   - 使用LinkedList<string> allApps = new LinkedList<string>()。 LinkedList确实可以非常快速地添加新项目。

List<string> Appname = new List<string>();列表也是如此。

其次:在开头你有distinct的列表,然后在foreach - 循环的每次迭代中转换为列表,而新构建的list不在该循环中使用。所以在这里你可以将distinct->tolist代码移出循环,代码逻辑不会改变,但性能会提高。

到目前为止,我可以建议以下代码:

 LinkedList<string> allApps2 = new LinkedList<string>();// linkedlist here
        roster = MURLEngine.GetUserFriendDetails(token, userId);
        var usersfriends = from elements in roster.RosterEntries
                           where elements[0] == 'm' && elements[1] >= '0' && elements[1] <= '9'
                           select elements;
        foreach (string userid in usersfriends)
        {
            roster = MURLEngine.GetUserFriendDetails(token, userid);
            var usersapps = from elements in roster.RosterEntries
                            where elements[0] != 'm'
                            select elements;
            foreach(var userapp in usersapps)// add _all the apps_ to list. Will be distinct-ed later
            {
                allApps2.AddLast(userapp);// don't worry, it works for O(1)
            }

        }

        var allApps = allApps2.Distinct().ToList();

        int countapps = 0;
        LinkedList<string> Appname2 = new LinkedList<string>();// linkedlist here
        countapps = appList.Count();

        for (int y = 0; y < countapps; y++)
        {
            foreach (string li in allApps)  // 
            {
                bool istrueapp = appList.ElementAt(y).AppName.Equals(li);
                if (istrueapp == true)
                {
                    Appname2.AddLast(appList.ElementAt(y).AppName);// and here
                }
            }
        }

        var AppName = Appname2.ToList();// and you've got you List<string> as the result

请尝试使用此代码并告诉我它是如何工作的(虽然我认为它的工作速度要快得多)。 希望这会有所帮助。

<强>更新
最后我回家了,抱歉拖延。我使用了一些代码并通过将最后for重写为此代码来加快速度:

foreach (var app in appList)
            {
                foreach (string li in allApps) 
                {
                    bool istrueapp = app.AppName.Equals(li);
                    if (istrueapp)
                    {
                        Appname2.AddLast(app.AppName);
                    }
                }
            }

这至少在我的机器(r)上提供了极大的加速 请检查您的环境是否更快 希望有所帮助。

答案 2 :(得分:0)

您应该将allApps保存在由appname键入的字典中。要检查应用程序是否存在于appList中,只需查找allApps.Contains(li)。

问题很可能源于最后一个for循环,其复杂性似乎是O(n ^ 2)。使用dictinoary应该将复杂性降低到O(n * logn),从而解决问题。

答案 3 :(得分:0)

正如在另一个答案中已经评论过的那样,List在处理大量元素和执行简单操作时效率不高。更简单的集合(例如,Array)代表在这些条件下更有效的解决方案。示例代码(适用于处理“普通”数组;您可以将它与当前列表一起使用,或者在开始使用时使用数组):

List<string> Appname = new List<string>();
roster = MURLEngine.GetUserFriendDetails(token, userId);
foreach (string item in roster.RosterEntries)
{
    if(item == null || item.Trim().Length < 1) continue;
    if (item[0] == 'm' && Convert.ToInt32(item[1]) >= 0 && Convert.ToInt32(item[1]) <= 9)
    {
        var roster2 = MURLEngine.GetUserFriendDetails(token, item);
        foreach (string item2 in roster2.RosterEntries)
        {
            if(item2 == null || item2.Trim().Length < 1) continue;
            if (item2[0] != 'm')
            {
                bool found = false;
                foreach (string app in appList)
                {
                    if(app == null || app.Trim().Length < 1) continue;
                    if (app.AppName == item2)
                    {
                        found = true;
                        break;
                    }
                }
                if (found) Appname.Add(item2);
            }
        }
    }
}

如您所见,我忽略了allApps的中间存储(也可以通过查询在您的版本中完成)。

此代码应该对原始版本提供明显的改进(主要是当您将列表转换为数组时)。如果有兴趣进一步加快这个代码的速度,你应该考虑重新设计输入方式的选项(从而避免大概耗时的东西:调用MURLEngine.GetUserFriendDetails两次)。最后,请记住,通过将(string app in appList) loop存储在List / Array中,您可以使用简单条件(AppNames.Contains(item2))替换AppNames