【问题标题】:which should be used: array vs linked list?应该使用哪个:数组与链表?
【发布时间】:2010-07-17 22:58:39
【问题描述】:

我计划在不使用Queue<T> 类的情况下实现有界队列。在阅读了ArraysLinkedList<T> 的优缺点之后,我更倾向于使用 Array 来实现队列功能。集合将是固定大小的。我只想在队列中添加和删除项目。

类似

public class BoundedQueue<T>
{
   private T[] queue;
   int queueSize;

   public BoundedQueue(int size)
   {
      this.queueSize = size;
      queue = new T[size + 1];
   }
}

而不是

public class BoundedQueue<T>
{
   private LinkedList<T> queue;
   int queueSize;

   public BoundedQueue(int size)
   {
      this.queueSize = size;
      queue = new LinkedList<T>();
   }
}

我选择 Array 是因为效率高,而且集合是固定大小的。想就此征求其他意见。谢谢。

【问题讨论】:

  • 速度与效率的经典案例。数组对于计算机来说会更快,但 LinkedList 很可能需要更少的代码行。决定权完全取决于您!

标签: c# arrays data-structures linked-list


【解决方案1】:

好吧,这将是一个错误。我猜你是前 C/C++ 程序员,std::list 为王。从表面上看,它在内存方面非常节俭,不能让列表比只分配你需要的内存更有效,对吧?是的,LinkedList 就是这样做的。

但不,它与 CPU 缓存的工作方式非常不兼容,它们真的喜欢数组并且讨厌指针。将垃圾收集器放在上面,非常有能力打包内存。

read-them-and-weep 基准测试are here。斯塔克。

【讨论】:

  • 很遗憾,链接已失效。这是我在web archive: 上能找到的最后一个
【解决方案2】:

您当然应该使用Queue&lt;T&gt;,但在问题中您说您不想使用队列,而是自己实现队列。您需要首先考虑此类的用例。如果您想快速实现某些东西,您可以使用LinkedList&lt;T&gt;,但对于通用库,您需要更快的东西。

您可以使用 .NET Reflector 查看它是如何在 .NET 中实现的。这些是它的字段:

private T[] _array;
private const int _DefaultCapacity = 4;
private static T[] _emptyArray;
private const int _GrowFactor = 200;
private int _head;
private const int _MinimumGrow = 4;
private const int _ShrinkThreshold = 0x20;
private int _size;
[NonSerialized]
private object _syncRoot;
private int _tail;
private int _version;

如您所见,它使用数组。对于与如何调整数组大小有关的许多字段,它也相当复杂。即使您正在实现一个有界数组,您也希望允许该数组大于容量以避免在内存中不断移动项目。

关于线程安全,这两种类型都没有提供任何保证。例如在LinkedList&lt;T&gt; 的文档中是这样说的:

这种类型不是线程安全的。如果 LinkedList 需要被多个线程访问,则需要实现各自的同步机制。

【讨论】:

  • 如何设置它的容量使用更少的内存?它在做同样的事情(保留内存)。
  • @Mark:从多线程的角度有什么想法吗?两种情况都一样吗?
  • @stackoverflowuser:除非您明确使用同步,否则这两种方法都不是线程安全的。 .NET 4 中有一些线程安全的容器。
  • @Mark:谢谢。知道 .net 类 Queue 是否已使用 Array 或 LinkedList 实现?
  • @stackoverflowuser:是的,您可以在 .NET Reflector 中看到这一点。这是一个 T 数组。
【解决方案3】:

我不确定您为什么要在内部排除使用Queue&lt;T&gt;,尤其是考虑到您准备使用LinkedList&lt;T&gt;(它们在同一个程序集中)。 Queue&lt;T&gt; 会给你最好的性能和内存使用。您的课程可能如下所示:

public class BoundedQueue<T>
{
    private Queue<T> _queue;
    private int _maxSize;

    public BoundedQueue(int maxSize)
    {
        if (maxSize <= 0)
            throw new ArgumentOutOfRangeException("maxSize");

        _queue = new Queue<T>(maxSize);
        _maxSize = maxSize;
    }

    public int Count
    {
        get { return _queue.Count; }
    }

    /// <summary>
    /// Adds a new item to the queue and, if the queue is at its
    /// maximum capacity, also removes the oldest item
    /// </summary>
    /// <returns>
    /// True if an item was dequeued during this operation;
    /// otherwise, false
    /// </returns>
    public bool EnqueueDequeue(T value, out T dequeued)
    {
        dequeued = default(T);
        bool dequeueOccurred = false;

        if (_queue.Count == _maxSize)
        {
            dequeued = _queue.Dequeue();
            dequeueOccurred = true;
        }

        _queue.Enqueue(value);

        return dequeueOccurred;
    }
}

但也许您有充分的理由排除我无法想到的 Queue&lt;T&gt; 类?

【讨论】:

  • 谢谢。我也不能使用 LinkedList :) 但只是为了查看 Array 和 LinkedList 实现之间花费的时间,我使用了它。我发现 Array 实现更快。如果 LinkedList 更快,我会在不使用开箱即用的 .net 类的情况下实现它。希望能解释我的情况。
【解决方案4】:

你可以使用一个数组,你只需要计算头元素的索引,或者每次添加东西时将所有内容向下移动。数组适合通过索引访问,链表适合下一个/上一个和快速插入。

例如,如果你有 [1,2,3,4,5],而头部是 1,你加 6,我猜你会从后面去掉 5,然后把 6 放在它的位置。 6 将是新的头,但数组的内容将是 [1,2,3,4,6]。

【讨论】:

    【解决方案5】:

    以下是同时使用数组和链表的主要区别/优势/劣势:

    数组: - 如果在末尾没有进行插入(以及删除),则将项目添加到数组可能会相对昂贵,因为必须移动所有数组元素。 - 如果在最后添加对象,则非常有效 - 访问元素非常快...只需指向地址!

    链表: - 在队列中的任何位置添加元素总是相同的时间成本,并且非常快 - 必须使用访问器(迭代器)来访问元素。

    所以你试图实现一个队列......但是什么样的队列? 这一切都取决于你会用它做什么。

    如果您实现先进先出(或后进后出)队列(如堆栈),最好使用链接列表,因为您始终可以使用相同的访问器来访问前端或后端你的名单。

    但是如果你想要一个队列并且必须不断地在不同的地方访问你的元素,那就去数组吧!

    根据我对您的任务的了解,我会推荐一个链接列表...但您会最清楚!

    只有当您的队列中开始有很多元素时,这才会成为问题。如果你保持在几千以下,它不会

    希望对你有帮助

    【讨论】:

    • 它是一个带 FIFO 的有界队列。
    • 因为它是有界的,你可以在不向下移动的情况下添加东西。只需跟踪 head 索引,并通过添加 head + index 并使用模数来找出任何元素的真实索引。
    【解决方案6】:

    当添加的元素超出其容量时,您的有界队列将如何表现?第一项会像这样[1, 2, 3] -&gt; [2, 3, 4] 被推出还是最后一项会像这样[1, 2, 3] -&gt; [1, 2, 4] 被替换?如果是前者,那么我建议使用 LinkedList。如果是后者,数组或 List 就可以了。我只是想问一下,因为您的对象的行为将决定适当的行动方案,而据我所知,这种行为从未被定义过。也许除了我之外的每个人都已经确切地知道您所说的“有界队列”是什么意思,但我不想假设。

    【讨论】:

    • 超出容量会抛出 queuefullexception。
    猜你喜欢
    • 2014-10-14
    • 2011-02-22
    • 2020-01-30
    • 2016-05-20
    • 1970-01-01
    • 2010-09-29
    • 1970-01-01
    • 1970-01-01
    • 2013-03-07
    相关资源
    最近更新 更多