2

I need a data structure like Queue. Only the first element can exit and new elements should be added to end of Queue. And also I need to access to last element.

The System.Collections.Queue has all functionality I need except the last one.

I wondering is there any built in data structure like this?

HaMeD
  • 349
  • 1
  • 14
  • 5
    linq might enumerate the entire thing to get to the last element. For more direct access, maybe extend Queue and override Enqueue to always save a reference to the last item. – David S. Oct 08 '14 at 13:22
  • Aye, I think it will infact always have to do that - `MyClassThatRememberTheLastEnque : Queue` is the best way to go – Alex K. Oct 08 '14 at 13:23
  • 1
    @AlexK. How I didn't think of linq?:D post it as an answer. – HaMeD Oct 08 '14 at 13:23
  • 1
    Having tested it, Linq's `Last` does indeed go over every item. However, I think one should invoke Knuth here and use it anyway *unless it turns out to be too slow*. – Bart van Nierop Oct 08 '14 at 13:32
  • 1
    @DavidS. The members of `Queue` aren't virtual; you cannot override them. – Servy Oct 08 '14 at 14:04
  • The correct data structure to use here is a Double Ended Queue (Deque). Sadly .NET doesn't have an implementation of such a data structure; leaving you needing to either implement your own or use a 3rd party implementation. – Servy Oct 08 '14 at 14:06
  • [Deque (by Stephen Cleary)](http://nitodeque.codeplex.com/) or Custom Queue which should be implemented as [Circual Buffer](http://en.wikipedia.org/wiki/Circular_buffer) or simply adapting [the code of Queue in .NET](http://www.dotnetframework.org/default.aspx/4@0/4@0/DEVDIV_TFS/Dev10/Releases/RTMRel/ndp/fx/src/CompMod/System/Collections/Generic/Queue@cs/1305376/Queue@cs) – firda Oct 08 '14 at 18:11

2 Answers2

2

The C# LinkedList class is exactly what you need.

See https://stackoverflow.com/a/1798358/2394945

Community
  • 1
  • 1
Moby Disk
  • 3,761
  • 1
  • 19
  • 38
  • 1
    You're likely to have dramatically lower performance using this over `Queue` due to the loss of memory locality. – Servy Oct 08 '14 at 14:02
  • @Servy: So Queue uses a List internally? So if the LinkedList becomes a performance bottleneck, would you suggest falling back to a List? – Moby Disk Oct 08 '14 at 14:23
  • Using a `List` in this case is a bad idea, as `RemoveAt(0)`, which is how you'd do `Dequeue`, has a time complexity of O(n). – Bart van Nierop Oct 08 '14 at 14:34
  • 1
    @MobyDisk `Queue` is backed with an array, to which a new one is allocated when needed, much like a `List` is, yes. Using a `List` or array would be possible, but you would need to mange circling the references yourself, as `Queue` does, in order to ensure O(1) operations. – Servy Oct 08 '14 at 14:39
  • @BartvanNierop You can implement array- or `List`-based queue with O(1) `Dequeue`. But you're right that you can't use `RemoveAt(0)` for that. – svick Oct 09 '14 at 19:49
1

Based on comments and other answers the best way is create my own Queue, but implementing a Queue with best performance is a duplicate(because it already exists in c#).

So I simply create a MyQueue class and use a System.Collections.Generic.Queue<T> as its inner data structure. here is the code:

/// <summary>
/// A generic first in first out data structure with access to last element.
/// </summary>
/// <typeparam name="T">The specific data type for this queue.</typeparam>
public class MyQueue<T>
{
    #region Varibles
    private Queue<T> _inner;
    private T _last;
    #endregion

    #region Properties

    /// <summary>
    /// Number of elements of this Queue.
    /// </summary>
    public int Count
    {
        get
        {
            return _inner.Count;
        }
    }

    /// <summary>
    /// The inner Queue in this structure.
    /// </summary>
    public Queue<T> Inner
    {
        get
        {
            return _inner;
        }
    }

    #endregion

    public MyQueue()
    {
        _inner = new Queue<T>();
    }

    #region Methods
    /// <summary>
    /// Adds an object to the end of the queue.
    /// </summary>
    /// <param name="item">Specific item for add.</param>
    public void Enqueue(T item)
    {
        _inner.Enqueue(item);
        _last = item;
    }

    /// <summary>
    /// Return and removes the first item in the queue.
    /// </summary>
    /// <returns>The first item in queue.</returns>
    /// <exception cref="InvalidOperationException">InvalidOperationException</exception>
    public T Dequeue()
    {
        if (_inner.Count > 0)
            return _inner.Dequeue();
        else
            throw new InvalidOperationException("The Queue is empty.");
    }

    /// <summary>
    /// Returns the first item in the queue without removing it.
    /// </summary>
    /// <returns>The first item in the queue.</returns>
    public T Peek()
    {
        return _inner.Peek();
    }

    /// <summary>
    /// Returns the last item in the queue without removing it.
    /// </summary>
    /// <returns>The last item in the queue.</returns>
    public T Last()
    {
        return _last;
    }

    /// <summary>
    /// Clears all items in the queue.
    /// </summary>
    public void Clear()
    {
        _inner.Clear();
    }
    #endregion
}
HaMeD
  • 349
  • 1
  • 14