应该使用哪个:数组vs链表?

时间:2010-07-17 22:58:40

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

我计划在不使用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。想就此得到其他意见。感谢。

6 个答案:

答案 0 :(得分:2)

嗯,这将是一个错误。我猜你是一名前C / C ++程序员,std :: list是王道。从表面上看,它的内存非常节俭,无法使列表可能比仅分配您需要的内存更有效,对吧?是的,LinkedList可以做到这一点。

但不,它与CPU缓存的工作方式非常不兼容,它们真的就像数组和仇恨指针一样。将垃圾收集器置于其上,因此能够打包内存。

读取和哭泣基准are here。斯塔克。

答案 1 :(得分:1)

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

您可以使用.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<T>的文档中,它说:

  

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

答案 2 :(得分:1)

我不确定你为什么要排除在内部使用Queue<T>,特别是考虑到你要使用LinkedList<T>(他们在同一个程序集中)。 Queue<T>将为您提供最佳性能和内存使用率。你的课可能看起来像这样:

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<T>课程?

答案 3 :(得分:0)

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

例如,如果你有[1,2,3,4,5],并且头部是1,那么你加6,你会在后面放下5个,然后放六个就位。 6将是新的头部,但阵列的内容将是[1,2,3,4,6]。

答案 4 :(得分:0)

以下是使用数组和链接列表的主要区别/优点/缺点:

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

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

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

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

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

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

如果您的队列中有大量元素,这只会是一个问题。如果你低于几千,它不会

希望有所帮助

答案 5 :(得分:0)

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