何时在数组/数组列表中使用链表?

时间:2008-12-26 06:52:53

标签: arrays list arraylist linked-list

我使用了很多列表和数组,但我还没有遇到过这样一种场景,即如果不比链表更容易使用数组列表那么容易。我希望有人能给我一些关于链表明显更好的例子。

15 个答案:

答案 0 :(得分:232)

在以下情况下,链接列表优于数组:

  1. 您需要从列表中进行恒定时间插入/删除(例如在时间可预测性非常关键的实时计算中)

  2. 您不知道列表中有多少项。对于数组,如果数组变得太大,您可能需要重新声明和复制内存

  3. 您不需要随机访问任何元素

  4. 您希望能够在列表中间插入项目(例如优先级队列)

  5. 在以下情况下优先使用数组:

    1. 您需要对元素进行索引/随机访问

    2. 提前知道数组中元素的数量,以便为数组分配正确的内存量

    3. 在迭代遍历所有元素时需要速度。您可以在数组上使用指针数学来访问每个元素,而您需要根据链表中每个元素的指针查找节点,这可能会导致页面错误,从而导致性能下降。

    4. 记忆是一个问题。填充数组占用的内存少于链表。数组中的每个元素都只是数据。每个链表节点都需要数据以及指向链表中其他元素的一个(或多个)指针。

    5. 数组列表(类似于.Net中的那些)为您提供了数组的好处,但为您动态分配资源,这样您就不必过多担心列表大小,并且可以毫不费力地删除任何索引处的项目或者重新洗牌。性能方面,arraylists比原始数组慢。

答案 1 :(得分:52)

数组具有O(1)随机访问权限,但是添加内容或从中删除内容非常昂贵。

链接列表在任何地方添加或删除项目以及迭代都非常便宜,但随机访问是O(n)。

答案 2 :(得分:16)

Algorithm           ArrayList   LinkedList
seek front            O(1)         O(1)
seek back             O(1)         O(1)
seek to index         O(1)         O(N)
insert at front       O(N)         O(1)
insert at back        O(1)         O(1)
insert after an item  O(N)         O(1)

ArrayLists适用于一次性多次写入或多次写入,但在前面或中间添加/删除时效果不佳。

答案 3 :(得分:14)

要添加到其他答案,大多数数组列表实现会在列表末尾保留额外的容量,以便可以在O(1)时间内将新元素添加到列表的末尾。当超出数组列表的容量时,将在内部分配一个更大的新数组,并复制所有旧元素。通常,新阵列的大小是旧阵列的两倍。这意味着平均,将新元素添加到数组列表的末尾是这些实现中的O(1)操作。因此,即使您事先不知道元素的数量,只要您在末尾添加元素,数组列表仍然可能比添加元素的链接列表更快。显然,在数组列表中的任意位置插入新元素仍然是O(n)操作。

访问数组列表中的元素也比链表快,即使访问是连续的。这是因为数组元素存储在连续的内存中,可以轻松缓存。链接列表节点可能分散在许多不同的页面上。

如果您知道要在任意位置插入或删除项目,我建议仅使用链接列表。几乎所有其他内容的数组列表都会更快。

答案 4 :(得分:7)

如果您需要在中间插入项目并且不想开始调整数组大小并改变周围的事物,则会出现列表的优点。

你是对的,因为通常情况并非如此。我有一些非常具体的案例,但不是太多。

答案 5 :(得分:2)

这些是Collection最常用的实现。

<强>的ArrayList:

  • 最后插入/删除O(1)最坏情况O(n)

  • 在中间插入/删除O(n)

  • 检索任何位置O(1)

<强>链表:

  • 在任何位置插入/删除O(1)(注意,如果你有对该元素的引用)

  • 在中间检索O(n)

  • 检索第一个或最后一个元素O(1)

矢量:不要使用它。它是一个类似于ArrayList的旧实现,但所有方法都是同步的。对于多线程环境中的共享列表,它不是正确的方法。

<强> HashMap中

在O(1)

中按键插入/删除/检索

<强> TreeSet中 插入/删除/包含在O(日志N)

<强> HashSet的 在O(1)

中插入/删除/包含/大小

答案 6 :(得分:2)

这一切都取决于你在迭代时所做的操作类型,所有数据结构都在时间和内存之间进行权衡,根据我们的需要,我们应该选择正确的DS。因此在某些情况下,LinkedList比数组快,反之亦然。考虑数据结构的三个基本操作。

  • 搜索

由于数组是基于索引的数据结构搜索array.get(index)将花费O(1)时间而链表不是索引DS所以你需要遍历索引,其中index&lt; = n,n是size链表的数据,因此当随机访问元素时,数组的链表更快。

问。这背后的美丽是什么?

由于Arrays是连续的内存块,它们的大块将在第一次访问时加载到缓存中,这使得访问数组的剩余元素相对较快,因为我们访问数组中的元素引用也增加了因此,较少捕获未命中,缓存局部性指的是缓存中的操作,因此与内存相比执行得更快,基本上在阵列中,我们最大化顺序元素访问在缓存中的机会。虽然链接列表不一定是连续的内存块,但不能保证列表中按顺序出现的项目实际上在内存中彼此靠近排列,这意味着更少的高速缓存命中率,例如更多缓存未命中,因为我们需要从内存中读取链接列表元素的每次访问,这会增加访问它们所需的时间并降低性能,因此如果我们进行更多随机访问操作(即搜索),则数组将快速执行,如下所述。 / p>

  • 插入

这在LinkedList中简单快捷,因为与数组相比,在LinkedList(在Java中)插入是O(1)操作,考虑到数组已满的情况,如果数组已满,我们需要将内容复制到新数组在最坏的情况下,将一个元素插入到O(n)的ArrayList中,而如果你在数组的末尾插入一些东西,ArrayList也需要更新它的索引,如果是链表,我们不需要调整它,你只需要更新指针。

  • 删除

它的作用类似于插入,在LinkedList中比在数组中更好。

答案 7 :(得分:0)

嗯,Arraylist可用于以下情况我猜:

  1. 您不确定会出现多少元素
  2. 但您需要通过索引随机访问所有元素
  3. 例如,您需要导入并访问联系人列表中的所有元素(其大小未知)

答案 8 :(得分:0)

使用链表进行Radix排序数组和多项式运算。

答案 9 :(得分:0)

1)如上所述,与ArrayList(O(n))相比,insertList和remove操作在LinkedList中提供了良好的性能(O(1))。因此,如果在应用程序中需要频繁添加和删除,那么LinkedList是最佳选择。

2)搜索(get方法)操作在Arraylist(O(1))中很快但在LinkedList(O(n))中没有,因此如果添加和删除操作较少且搜索操作要求较多,则ArrayList将是您的最好的选择。

答案 10 :(得分:0)

我认为主要区别在于您是否经常需要在列表顶部插入或删除内容。

对于数组,如果从列表顶部删除某些内容而不是复杂度为o(n),因为数组元素的所有索引都必须移位。

使用链接列表,它是o(1),因为您只需要创建节点,重新分配头部并将引用分配给下一个头部。

当在列表的末尾经常插入或删除时,数组是首选,因为复杂性将是o(1),不需要重新索引,但对于链表,它将是o(n),因为你需要去从头到最后一个节点。

我认为在链表和数组中搜索都是o(log n)因为你可能正在使用二进制搜索。

答案 11 :(得分:0)

我做了一些基准测试,发现列表类实际上比LinkedList更快地随机插入:

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

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int count = 20000;
            Random rand = new Random(12345);

            Stopwatch watch = Stopwatch.StartNew();
            LinkedList<int> ll = new LinkedList<int>();
            ll.AddLast(0);
            for (int i = 1; i < count; i++)
            {
                ll.AddBefore(ll.Find(rand.Next(i)),i);

            }
            Console.WriteLine("LinkedList/Random Add: {0}ms", watch.ElapsedMilliseconds);

            watch = Stopwatch.StartNew();
            List<int> list = new List<int>();
            list.Add(0);
            for (int i = 1; i < count; i++)
            {
                list.Insert(list.IndexOf(rand.Next(i)), i);

            }
            Console.WriteLine("List/Random Add: {0}ms", watch.ElapsedMilliseconds);

            Console.ReadLine();
        }
    }
}

链表需要900毫秒,列表类需要100毫秒。

它创建后续整数的列表。每个新整数都插入到列表中已有的随机数之后。 也许List类使用的东西不仅仅是数组。

答案 12 :(得分:0)

实际上,内存位置在实际处理中具有巨大的性能影响。

“大数据”处理与随机访问相比,磁盘流的使用越来越多,这表明围绕此方案构建应用程序可以大大提高性能。

如果有任何方法可以按顺序访问数组,那么这是最佳表现。如果表现很重要,至少应考虑以此为目标进行设计。

答案 13 :(得分:0)

到目前为止,数组是使用最广泛的数据结构。但是,链表以其独特的方式被证明是有用的,其中数组笨拙-至少可以说是昂贵的。

链接列表对于在大小可能有所变化的情况下实现堆栈和队列很有用。可以推送或弹出链接列表中的每个节点,而不会打扰大多数节点。在中间某处插入/删除节点也是如此。但是,在数组中,所有元素都必须移位,这在执行时间方面是一项昂贵的工作。

二叉树和二叉搜索树,哈希表和尝试是其中的一些数据结构-至少在C语言中,您需要链表作为构建它们的基本要素。

但是,在期望能够通过其索引调用任意元素的情况下,应避免使用链表。

答案 14 :(得分:0)

可以使用以下几点来简单地回答问题:

  1. 当需要收集相似类型的数据元素时,将使用数组。而链表是称为节点的混合类型数据链元素的集合。

  2. 在数组中,可以在O(1)时间内访问任何元素。而在链表中,我们需要花费O(n)的时间从头到需要的节点遍历整个链表。

  3. 对于数组,最初需要声明特定大小。但是链接列表的大小是动态的。