-1

I want to use a List as a queue. This is because List is more flexible when getting/inserting data.

My question is when I use Linq operations (DistinctBy, Take) as shown below, do I still get the elements in order they have been added into the list?

private List<IMessage> _queue = new List<IMessage>(); // add many _queue.Add(tagMessage); toConsume = _queue .Where(t => !_snLockList.Contains(t.Id)) .DistinctBy(t => t.Id) .Take(freeTasks).ToList(); 
12
  • 5
    Does the title of the question has any relevance with what is asked inside? You are basically asking about the behavior of the LINQ operators Where, DistinctBy, Take and ToList, whether they preserve the order of the source sequence or not. It doesn't matter if the source is a list, a queue, or whatever other collection. Commented May 27, 2024 at 6:20
  • If you only add elements at the end of the list (using .Add() or .AddRange(), the added elements (less those that are later removed) will remain in the order added. If you insert any elements at a specific index using .Insert() or .InsertRange(), they will remain in the same position relative to the elements before and after. Be aware that List<> is implemented using an array under the covers, so insert and remove operations at other than the end of the list can be costly, as it involves moving elements around in the array. Commented May 27, 2024 at 6:22
  • 2
    Without an order by or a skip the LINQ will take the distinct items where they're not in the _snLockList list. I don't understand why you wouldn't just Console/Debug.Writeline() and see for yourself testing a few scenarios? Commented May 27, 2024 at 6:23
  • 6
    List is not thread-safe(!) - it neither requires stress nor special cases for your implementation to go up in smoke. Have a look at thread-safe implementations of IList before worrying about particular LINQ-operations Commented May 27, 2024 at 6:49
  • 1
    LINQ treats everything as an IEnumerable<T> the results will depend on the implementation of the relevant enumerator. In practice, that will almost always lead to items output in the same order as they are input unless the operation specifically requires re/disordering. That said, unless it is specifically documented, this is just a result of convenience rather than necessity and there's no guarantee that that won't change in future. Again, though, it's unlikely to ever change in practice. That said, if you specifically require an order then you should implement that yourself. Commented May 27, 2024 at 6:52

1 Answer 1

2

The answer is "Yes but...".

Yes all LINQ operations that iterate over collections using "foreach, yield return pattern" return results in the order of the underlying object's (your List<T>'s) IEnumerable (see MS Doc - yield, also follow link in this article to iterators) - unless/until another operation reorders (Sort()).

But you do not give enough insight into the "what, how and why" of what you are doing... (1) Lists can be used to implement pretty efficient (ring) queues ("ring buffers" - for which you will probably have to code iterator/s). (2) If you intend inserting into and deleting from the middle of the queue, then you must use (O(n), i.e. pretty expensive) shuffle-up and -down logic (behind the scenes of Insert(), RemoveAt()... and your queue isn't FIFO i.e. isn't really a queue).

Have you considered using PriorityQueue<T> or LinkedList<T>?

Sign up to request clarification or add additional context in comments.

2 Comments

I use _queue.Add(tagMessage);, _queue.Insert(index, tagMessage); and then the above linq operation to select a few items and later I remove them _queue.RemoveAll(t => toConsume.Any(x => x.QueueId == t.QueueId));. These are the operations I use. I want the list to keep the index order. It's not a strict queue. All operations are done under a lock because of multi-threaded access.
I see the question has been closed (duplication on the LINQ-query-ordering aspect). If the queue can get large and/or performance is important (and/or you are OCD about design like me;-) then from your description I'd seriously consider LinkedList<T> with a policy of "add at the tail and consume first available from the head/front". The benefit being O(1) insertion and removal operations at the (probably very much smaller) cost of some memory fragmentation (.NET's memory management is superb). (Possibly also trading away some of the tidiness and terseness of LINQ.)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.