3

I am wondering about a certain functionality in C#...

I would like to have a List<Object> MyList();, which I could .Add(new Object()) finite amount of times. Let's say I added 5 items, and if I would add sixth, then the last item would be destroyed, and this sixth element would be put on top of the list.

Is there any built-in mechanism in c# that does that?

ojek
  • 9,680
  • 21
  • 71
  • 110
  • 1
    "last item" is rather ambiguous. Do you mean latest or eldest? – Jeroen Vannevel Dec 03 '13 at 20:44
  • 1
    do you mean like a circularbuffer? – Daniel A. White Dec 03 '13 at 20:45
  • that doesn't exist, but there are a ton of implementations out there. – Daniel A. White Dec 03 '13 at 20:46
  • Term is `Circular Buffer`, there is no native support for it. You'd need to write a custom wrapper around `Queue` – Konstantin Dec 03 '13 at 20:50
  • @kostyan This isn't what a Circular Buffer is. A circular buffer is simply an array with pointers to what should be treated as the head and tail; in most implementations you would ensure that it *doesn't* allow people to overwrite other items when adding. It could be an appropriate data structure to use in implementing this concept, but you can use a circular buffer to implement other behaviors, and you can implement this behavior with other underlying implementations. – Servy Dec 03 '13 at 20:53
  • Just google `PriorityQueue`. I think it will fit your needs and there many many good implementations out there. – L.B Dec 03 '13 at 21:15

4 Answers4

3

There is no build-in collection in Framework as Servy said. However, you can make a CircularBuffer like this -

enter image description here

namespace DataStructures
{
    class Program
    {
        static void Main(string[] args)
        {
            var buffer = new CircularBuffer<int>(capacity: 3);

            while (true)
            {
                int value;
                var input = Console.ReadLine();

                if (int.TryParse(input, out value))
                {
                    buffer.Write(value);
                    continue;
                }
                break;
            }

            Console.WriteLine("Buffer: ");
            while (!buffer.IsEmpty)
            {
                Console.WriteLine(buffer.Read());
            }
            Console.ReadLine();
        }
    }
}

namespace DataStructures
{
    public class CircularBuffer<T>
    {
        private T[] _buffer;
        private int _start;
        private int _end;

        public CircularBuffer()
            : this(capacity: 3)
        {
        }

        public CircularBuffer(int capacity)
        {
            _buffer = new T[capacity + 1];
            _start = 0;
            _end = 0;
        }

        public void Write(T value)
        {
            _buffer[_end] = value;
            _end = (_end + 1) % _buffer.Length;
            if (_end == _start)
            {
                _start = (_start + 1) % _buffer.Length;
            }
        }

        public T Read()
        {
            T result = _buffer[_start];
            _start = (_start + 1) % _buffer.Length;
            return result;
        }

        public int Capacity
        {
            get { return _buffer.Length; }
        }

        public bool IsEmpty
        {
            get { return _end == _start; }
        }

        public bool IsFull
        {
            get { return (_end + 1) % _buffer.Length == _start; }
        }
    }
}

Above code is from PluralSight - Scott Allen's C# Generics.

Win
  • 61,100
  • 13
  • 102
  • 181
2

In my core library, I have something called a LimitedQueue<T>. This is probably similar to what you're after (you could easily modify it to be a List<T> instead). (Source on GitHub)

using System.Collections.Generic;

namespace Molten.Core
{
    /// <summary>
    /// Represents a limited set of first-in, first-out objects.
    /// </summary>
    /// <typeparam name="T">The type of each object to store.</typeparam>
    public class LimitedQueue<T> : Queue<T>
    {
        /// <summary>
        /// Stores the local limit instance.
        /// </summary>
        private int limit = -1;

        /// <summary>
        /// Sets the limit of this LimitedQueue. If the new limit is greater than the count of items in the queue, the queue will be trimmed.
        /// </summary>
        public int Limit
        {
            get
            {
                return limit;
            }
            set
            {
                limit = value;
                while (Count > limit)
                {
                    Dequeue();
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the LimitedQueue class.
        /// </summary>
        /// <param name="limit">The maximum number of items to store.</param>
        public LimitedQueue(int limit)
            : base(limit)
        {
            this.Limit = limit;
        }

        /// <summary>
        /// Adds a new item to the queue. After adding the item, if the count of items is greater than the limit, the first item in the queue is removed.
        /// </summary>
        /// <param name="item">The item to add.</param>
        public new void Enqueue(T item)
        {
            while (Count >= limit)
            {
                Dequeue();
            }
            base.Enqueue(item);
        }
    }
}

You would use it like this:

LimitedQueue<int> numbers = new LimitedQueue<int>(5);
numbers.Enqueue(1);
numbers.Enqueue(2);
numbers.Enqueue(3);
numbers.Enqueue(4);
numbers.Enqueue(5);
numbers.Enqueue(6); // This will remove "1" from the list
// Here, "numbers" contains 2, 3, 4, 5, 6 (but not 1).
qJake
  • 16,821
  • 17
  • 83
  • 135
  • 1
    You really shouldn't extend `Queue`, or other collections, to modify functionality such as this, given that the methods you need to affect aren't `virtual`. The ability to have the object typed as the super type lets you violate the invariants this type intends to hold, which is generally a pretty big problem. – Servy Dec 03 '13 at 20:48
  • And, as I have commented on another post: this will not allow random removal. It was clearly specified to be a `List`. – Jeroen Vannevel Dec 03 '13 at 20:50
  • @Servy Yes, but generally speaking if you are maintaining the codebase (vs. allowing plugins from third parties or something like that) you would know how to use this class properly and it would be fine. Also, if Microsoft really intended for people to not extend from the base collection types, they would have `sealed` them. ;) – qJake Dec 03 '13 at 20:52
  • @JeroenVannevel Correct, but the code is easily modifiable to extend from `List` or implement `IList` instead. – qJake Dec 03 '13 at 20:56
  • 1
    @SpikeX Well, I really wish the *had* sealed them. There is very little use in ever extending them given that none of the useful methods are virtual. Many people don't really understand the semantics of shadowed methods, or won't realize/remember that the methods are shadowed, which can cause problems. It's not the fear of malicious code so much as the reasonably probable cases of developers simply making a mistake and, for example, passing this collection to a method accepting a `Queue` that adds a bunch of items to it. It's really quite easy to *unintentionally* break this code. – Servy Dec 03 '13 at 20:56
  • 1
    If Microsoft really intended for people to extend from `Queue`, they would have made `Enqueue` virtual. If you change your sample to `Queue numbers = new LimitedQueue(5);`, you can add as many ints as you want. – Austin Salonen Dec 03 '13 at 20:58
  • Fair enough, I'll make a note to change it to implement `IList` or `ICollection` instead (or, any of you could, it's open source!). – qJake Dec 03 '13 at 20:59
2

None of the built in collections will do this, but you can easily make your own class that has an internal list that has this behavior when adding an item. It's not particularly difficult, but writing out all of the methods that a standard list would use and implementing all of the interfaces List does could be a bit tedious.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • Easiest would be to just wrap the adding to the list in a method and direct it from there. – Jeroen Vannevel Dec 03 '13 at 20:47
  • @JeroenVannevel If this is only used in a fairly small scope, and all internal to the application, then potentially, yes. It becomes a problem if you want to expose the collection to a wider code base, or even as a part of a public API. – Servy Dec 03 '13 at 20:50
-1

You can use a Queue with a fixed size. Just call .ToList() afterwards.

Fixed size queue which automatically dequeues old values upon new enques

Community
  • 1
  • 1
Tim P.
  • 2,903
  • 24
  • 26
  • The code in that post is sub-par in my opinion, it doesn't even extend a base .NET collection class. (I was not the one who downvoted you, though.) – qJake Dec 03 '13 at 20:49
  • 1
    @SpikeX [It's a *good* thing that it doesn't extend a base .NET collection.](http://stackoverflow.com/questions/20361269/c-sharp-finite-list-or-limited-list#comment30397119_20361327) Of course, some of the answers have other problems, such as locking on the implicit object reference. – Servy Dec 03 '13 at 21:05
  • @Servy Right, as I have since learned. ;) (I can't edit my comment anymore, unfortunately.) – qJake Dec 03 '13 at 21:06