c# Adding a Remove(int index) method to the .NET Queue class

前端 未结 12 1524
攒了一身酷
攒了一身酷 2021-01-07 17:14

I would like to use the generic queue class as described in the .NET framework (3.5) but I will need a Remove(int index) method to remove items from the queue. Can I achieve

相关标签:
12条回答
  • 2021-01-07 17:30

    Although there isn't a built-in way, you shouldn't use a List structure or other structure, IFF RemoveAt isn't a frequent operation.

    If you are normally enqueuing and dequeuing but only occasionally removing, then you should be able to afford a queue rebuild when removing.

    public static void Remove<T>(this Queue<T> queue, T itemToRemove) where T : class
    {
        var list = queue.ToList(); //Needs to be copy, so we can clear the queue
        queue.Clear();
        foreach (var item in list)
        {
            if (item == itemToRemove)
                continue;
    
            queue.Enqueue(item);
        }
    }
    
    public static void RemoveAt<T>(this Queue<T> queue, int itemIndex) 
    {
        var list = queue.ToList(); //Needs to be copy, so we can clear the queue
        queue.Clear();
    
        for (int i = 0; i < list.Count; i++)
        {
            if (i == itemIndex)
                continue;
    
            queue.Enqueue(list[i]);
        }
    }
    

    The following approach might be more efficient, using less memory, and thus less GC:

    public static void RemoveAt<T>(this Queue<T> queue, int itemIndex)
    {
        var cycleAmount = queue.Count;
    
        for (int i = 0; i < cycleAmount; i++)
        {
            T item = queue.Dequeue();
            if (i == itemIndex)
                continue;
    
            queue.Enqueue(item);
        }
    }
    
    0 讨论(0)
  • 2021-01-07 17:34

    I do not believe we should be using List<T> to emulate a queue, for a queue, the enqueue and dequeue operations should be very highly performant, which they would not be when using a List<T>. For the RemoveAt method however, it is acceptable to be non-performant, as it is not the primary purpose of a Queue<T>.

    My approach at implementing RemoveAt is O(n) but the queue still maintains a largely O(1) enqueue (sometimes the internal array needs reallocating which makes the operations O(n)) and always O(1) dequeue.

    Here is my implementation of a RemoveAt(int) extension method for a Queue<T>:

    public static void RemoveAt<T>(this Queue<T> queue, int index)
    {
        Contract.Requires(queue != null);
        Contract.Requires(index >= 0);
        Contract.Requires(index < queue.Count);
    
        var i = 0;
    
        // Move all the items before the one to remove to the back
        for (; i < index; ++i)
        {
            queue.MoveHeadToTail();
        }
    
        // Remove the item at the index
        queue.Dequeue();
    
        // Move all subsequent items to the tail end of the queue.
        var queueCount = queue.Count;
        for (; i < queueCount; ++i)
        {
            queue.MoveHeadToTail();
        }
    }
    

    Where MoveHeadToTail is defined as follows:

    private static void MoveHeadToTail<T>(this Queue<T> queue)
    {
        Contract.Requires(queue != null);
    
        var dequed = queue.Dequeue();
        queue.Enqueue(dequed);
    }
    

    This implementation also modifies the actual Queue<T> rather than returning a new Queue<T> (which I think is more in-keeping with other RemoveAt implementations).

    0 讨论(0)
  • 2021-01-07 17:38

    In fact, this defeats the whole purpose of Queue and the class you'll eventually come up with the will violate the FIFO semantics altogether.

    0 讨论(0)
  • 2021-01-07 17:40

    The queue class is so difficult to understand. Use a generic list instead.

    0 讨论(0)
  • 2021-01-07 17:45

    What you want is a List<T> where you always call RemoveAt(0) when you want to get the item from the Queue. Everything else is the same, really (calling Add would add an item to the end of the Queue).

    0 讨论(0)
  • 2021-01-07 17:45

    Note that with a list you can make the "removal" process more efficient if you don't actually remove the item but merely "mark" it as "removed". Yes, you have to add a bit of code to deal with how you've done it, but the payoff is the efficiency.

    Just as one example - Say you have a List<string>. Then you can, for example, just set that particular item to null and be done with it.

    0 讨论(0)
提交回复
热议问题