17

First of all, just grant that I do in fact want the functionality of a Queue<T> -- FIFO, generally only need Enqueue/Dequeue, etc. -- and so I'd prefer an answer other than "What you really want is a List<T>" (I know about RemoveAt).

For example, say I have a Queue<DataPoint> dataToProcess of data points that need to be processed in the order in which they arrived. Then periodically it would make sense to have some code like this:

while (dataToProcess.Count > 0) {
    DataPoint pointToProcess = dataToProcess.Dequeue();
    ProcessDataPoint(pointToProcess);
}

But then suppose, for whatever reason, it's discovered that a particular data point which has been added to the queue should not be processed. Then it would be ideal if there were a method analogous to:

dataToProcess.Remove(badPoint);

I understand that there's really no feasible way to have a Remove method that does not involve some form of enumeration; however, since a Queue<T> doesn't really let you just walk in and remove some item randomly, the only solution I could figure out was this:

bool Remove(T item) {
    bool itemFound = false;

    // set up a temporary queue to take items out
    // one by one
    Queue<T> receivingQueue = new Queue<T>();

    // move all non-matching items out into the
    // temporary queue
    while (this.Count > 0) {
        T next = this.Dequeue();
        if (next.Equals(item)) {
            itemFound = true;
        } else {
            receivingQueue.Enqueue(next);
        }
    }

    // return the items back into the original
    // queue
    while (receivingQueue.Count > 0) {
        this.Enqueue(receivingQueue.Dequeue());
    }

    return itemFound;
}

Is this ridiculous? It certainly looks bad, but I can't really see a better way, other than writing a custom class. And even then, the best way I could think to implement a Remove method would be to use a LinkedList<T> internally.

Dan Tao
  • 125,917
  • 54
  • 300
  • 447

5 Answers5

27

I think switching over to a new custom class that had a LinkedList internally would only take you a few minutes and would be much more performant than what you have now.

public class SpecialQueue<T>
{
    LinkedList<T> list = new LinkedList<T>();

    public void Enqueue(T t)
    {
        list.AddLast(t);
    }

    public T Dequeue()
    {
        var result = list.First.Value;
        list.RemoveFirst();
        return result;
    }

    public T Peek()
    {
        return list.First.Value;
    }

    public bool Remove(T t)
    {
        return list.Remove(t);
    }

            public int Count { get { return list.Count; } }
}
Ben Gripka
  • 16,012
  • 6
  • 45
  • 41
Jake Pearson
  • 27,069
  • 12
  • 75
  • 95
  • I guess I would tend to agree with you, which is why I asked this question--because I can't think of a good way to go about doing this with a `Queue`, but I thought maybe someone else would be able to see something I was missing. – Dan Tao Oct 20 '09 at 12:59
  • Also, you can speed up your existing method by enqueueing as you dequeue instead of saving your queue to a temporary list. – Jake Pearson Oct 20 '09 at 13:02
  • @Jake: quite clever -- hadn't thought of that! Though your `LinkedList` implementation certainly does seem better (and matches what I would have done... I guess that's pretty much *the* way to do it, after all). – Dan Tao Oct 20 '09 at 13:05
9

An alternative could be to just leave the items in the queue, and ignore them when you are reading from it. Something like:

T DequeueFiltered(HashSet<T> ignored) {
   T item;
   while (ignored.Contains(item = Dequeue())) {
      ignored.Remove(item);
   }
   return item;
}
Benjol
  • 63,995
  • 54
  • 186
  • 268
Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • 3
    I find this a very smart suggestion. One issue I can see is that, since the item is still *in* the queue, there'd have to be a filtered form of `GetEnumerator` as well, to skip the ignored items during enumeration. – Dan Tao Oct 20 '09 at 13:09
1

I am a beginner however I managed to solve the same (or a very similar) issue recently. I hope it helps.

Here is our queue:

Queue<T> dataToProcess = new Queue<T>();

What if we put a copy of all enqueued items into a hashset (e.g.:

HashSet<T> pointsToProcess = new HashSet<T>(); 

so when enqueueing the elements we also add the same data to the hashset.

and When it turns out we don't need an element, we remove it from that hashset

pointsToProcess.Remove(element);

so when we have to 'use' the next element of the queue,

we examine whether we really need to deal with it (if it's member of the hashset) otherwise it has to be ignored and we can get rid of it,

while (dataToProcess.Count > 0) {


if  pointsToProcess.Contains(dataToProcess.Peek())
{


// processing data


}

else
{

// error message and

dataToProcess.Dequeue();

}


}
vampierre
  • 11
  • 1
1

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

Queue is the most efficient structure for queuing, implementing a queue on a list data structure is not efficient.

Although there isn't a built-in way, you shouldn't use a List structure or other structure, IFF Remove 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.

see the link for two simple extension methods

public static void Remove<T>(this Queue<T> queue, T itemToRemove) where T : class

public static void RemoveAt<T>(this Queue<T> queue, int itemIndex) where T : class

Community
  • 1
  • 1
Kind Contributor
  • 17,547
  • 6
  • 53
  • 70
-2
var first = Q.Dequeue();
if (first.Id.Equals(deleteId)) return;
Q.Enqueue(first);

while (Q.Peek() != first)
{
   var r = Q.Dequeue();
   if(!r.Id.Equals(deleteId)) Q.Enqueue(r);
} 
jenspo
  • 504
  • 4
  • 11