Is there a better way to implement a Remove method for a Queue?
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(receivi开发者_JS百科ngQueue.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.
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; } }
}
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;
}
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();
}
}
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
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);
}
精彩评论