【问题标题】:What is the performace difference in using List<T> vs LinkedList<T> say in the (c#) library [duplicate]在(c#)库中使用 List<T> 与 LinkedList<T> 的性能差异是什么[重复]
【发布时间】:2011-08-18 08:39:08
【问题描述】:

可能重复:
When should I use a List vs a LinkedList

这个问题与我之前合并的问题有关:与List vs LinkedList相关

如果我不希望对我的数据结构使用按索引访问,那么通过使用 LinkedList 而不是 List 可以节省多少?如果不是 100% 确定我永远不会使用索引访问,我想知道其中的区别。

假设我有 N 个实例。在 LinkedList 中插入和删除只会是 o(1) op ,而在 List 中它可能是 O(n),但由于它已优化,因此很高兴知道 n 的某些值有什么区别。假设 N = 1,000,000 和 N = 1,000,000,000

【问题讨论】:

  • 只需编写一个简单的控制台应用程序并使用StopWatch 即可查看差异。相信我,这需要 5 分钟。
  • 仅供参考,List&lt;T&gt; 是 .NET 的一部分,而不是“C# 库”的一部分

标签: c# performance list linked-list


【解决方案1】:

好的,我做了这个实验,结果如下:

这是包含 1000,000 个项目的列表和链表的经过时间:

LinkedList 500 insert/remove operations: 10171
List 500 insert/remove operations: 968465

与 1000,000 个项目相比,链表的速度快了 100 倍


代码如下:

    static void Main(string[] args)
    {

        const int N = 1000*1000;
        Random r = new Random();
        LinkedList<int> linkedList = new LinkedList<int>();
        List<int> list = new List<int>();
        List<LinkedListNode<int>> linkedListNodes = new List<LinkedListNode<int>>();

        for (int i = 0; i < N; i++)
        {
            list.Add(r.Next());
            LinkedListNode<int> linkedListNode = linkedList.AddFirst(r.Next());
            if(r.Next() % 997 == 0)
                linkedListNodes.Add(linkedListNode);
        }

        Stopwatch stopwatch = new Stopwatch();

        stopwatch.Start();
        for (int i = 0; i < 500; i++)
        {
            linkedList.AddBefore(linkedListNodes[i], r.Next());
            linkedList.Remove(linkedListNodes[i]);
        }
        stopwatch.Stop();
        Console.WriteLine("LinkedList 500 insert/remove operations: {0}", stopwatch.ElapsedTicks);
        stopwatch.Reset();

        stopwatch.Start();
        for (int i = 0; i < 500; i++)
        {
            list.Insert(r.Next(0,list.Count), r.Next());
            list.RemoveAt(r.Next(0, list.Count));
        }
        stopwatch.Stop();
        Console.WriteLine("List 500 insert/remove operations: {0}", stopwatch.ElapsedTicks);


        Console.Read();
    }
}

【讨论】:

    【解决方案2】:

    别担心。像往常一样编写您的应用程序。完成后,使用真实数据运行几次并分析性能。换掉你正在使用的课程并比较结果。

    【讨论】:

      【解决方案3】:

      List 在内部使用一个数组,它总是有一个固定的大小,例如它使用 16 的大小(通常是 2 的幂),所以即使你存储一个项目,它也会保存大小为 16 的数组,其中 15 为空。

      现在假设,当您存储所有 16 个项目并尝试添加第 17 个项目时,它现在将创建一个 32 的新数组 (16 + 16),将所有 16 从旧数组复制到新数组,然后放入项目在 32 大小的数组中的第 17 位。

      现在,当您从第一个位置移除一个项目时,第一个项目之后的所有项目都会向前移动一步,因此第 2 个变为第 1 个,第 3 个变为第 2 个,依此类推。

      链表是由指针组成的,它不是数组,它只占用您创建的节点,并存储其上一个和下一个节点的引用。

      对于性能问题,如果您很少添加/删除项目,但您会非常频繁地迭代(读取整个列表或通过索引访问项目),List 将非常有用。 List 对于随机访问场景会更好,例如您将能够通过指定索引非常快速地获取项目。

      如果您要非常频繁地添加/删除项目,链接列表将非常有用,但您很少会迭代它(整个列表)。链表将更适合顺序访问,您将从当前节点向后或向前导航,但当您尝试查找索引项时,它的性能会很差。

      【讨论】:

        【解决方案4】:

        List&lt;T&gt; 中的反向插入实际上在amortized constant time 中运行。但是,删除元素的成本很高 (O(n))。

        【讨论】:

          猜你喜欢
          • 2011-08-17
          • 1970-01-01
          • 1970-01-01
          • 2013-12-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多