Как бы вы кодировали эффективный кольцевой буфер в Java или C #? - PullRequest
43 голосов
/ 26 февраля 2009

Мне нужен простой класс, который реализует фиксированный размер кольцевой буфер . Это должно быть эффективно, легко для глаз, типично напечатано.

На данный момент он не должен быть с поддержкой МТ . Я всегда могу добавить блокировку позже, в любом случае это не будет высокий параллелизм.

Методы должны быть: .Add() и, я думаю, .List(), где я получаю все записи. Во-вторых, поиск, я думаю, должен быть сделан с помощью индексатора. В любой момент я захочу получить любой элемент в буфере по index . Но имейте в виду, что от одного момента к следующему элемент [n] может отличаться, так как круговой буфер заполняется и переворачивается. Это не стек , это круговой буфер.

Относительно " переполнения ": я ожидаю, что внутри будет массив, содержащий элементы, и со временем head и tail буфера вращаться вокруг этого фиксированного массива. Но это должно быть невидимым для пользователя. Не должно быть обнаруживаемого извне события или поведения «переполнения».

Это не школьное задание - чаще всего оно будет использоваться для MRU-кэша или журнала транзакций или событий фиксированного размера.

Ответы [ 13 ]

0 голосов
/ 27 апреля 2014

Вот реализация, которую я написал для собственного использования, но она может быть полезна.

Буфер содержит максимально фиксированный набор элементов. Набор круговой, старые предметы автоматически удаляются. Вызывающая сторона может получить элементы хвостом по абсолютному инкрементному индексу (long), но элементы могут быть потеряны между вызовами, слишком удаленными во времени. Этот класс полностью потокобезопасен.

public sealed class ConcurrentCircularBuffer<T> : ICollection<T>
{
    private T[] _items;
    private int _index;
    private bool _full;

    public ConcurrentCircularBuffer(int capacity)
    {
        if (capacity <= 1) // need at least two items
            throw new ArgumentException(null, "capacity");

        Capacity = capacity;
        _items = new T[capacity];
    }

    public int Capacity { get; private set; }
    public long TotalCount { get; private set; }

    public int Count
    {
        get
        {
            lock (SyncObject) // full & _index need to be in sync
            {
                return _full ? Capacity : _index;
            }
        }
    }

    public void AddRange(IEnumerable<T> items)
    {
        if (items == null)
            return;

        lock (SyncObject)
        {
            foreach (var item in items)
            {
                AddWithLock(item);
            }
        }
    }

    private void AddWithLock(T item)
    {
        _items[_index] = item;
        _index++;
        if (_index == Capacity)
        {
            _full = true;
            _index = 0;
        }
        TotalCount++;
    }

    public void Add(T item)
    {
        lock (SyncObject)
        {
            AddWithLock(item);
        }
    }

    public void Clear()
    {
        lock (SyncObject)
        {
            _items = new T[Capacity];
            _index = 0;
            _full = false;
            TotalCount = 0;
        }
    }

    // this gives raw access to the underlying buffer. not sure I should keep that
    public T this[int index]
    {
        get
        {
            return _items[index];
        }
    }

    public T[] GetTail(long startIndex)
    {
        long lostCount;
        return GetTail(startIndex, out lostCount);
    }

    public T[] GetTail(long startIndex, out long lostCount)
    {
        if (startIndex < 0 || startIndex >= TotalCount)
            throw new ArgumentOutOfRangeException("startIndex");

        T[] array = ToArray();
        lostCount = (TotalCount - Count) - startIndex;
        if (lostCount >= 0)
            return array;

        lostCount = 0;

        // this maybe could optimized to not allocate the initial array
        // but in multi-threading environment, I suppose this is arguable (and more difficult).
        T[] chunk = new T[TotalCount - startIndex];
        Array.Copy(array, array.Length - (TotalCount - startIndex), chunk, 0, chunk.Length);
        return chunk;
    }

    public T[] ToArray()
    {
        lock (SyncObject)
        {
            T[] items = new T[_full ? Capacity : _index];
            if (_full)
            {
                if (_index == 0)
                {
                    Array.Copy(_items, items, Capacity);
                }
                else
                {
                    Array.Copy(_items, _index, items, 0, Capacity - _index);
                    Array.Copy(_items, 0, items, Capacity - _index, _index);
                }
            }
            else if (_index > 0)
            {
                Array.Copy(_items, items, _index);
            }
            return items;
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return ToArray().AsEnumerable().GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    bool ICollection<T>.Contains(T item)
    {
        return _items.Contains(item);
    }

    void ICollection<T>.CopyTo(T[] array, int arrayIndex)
    {
        if (array == null)
            throw new ArgumentNullException("array");

        if (array.Rank != 1)
            throw new ArgumentException(null, "array");

        if (arrayIndex < 0)
            throw new ArgumentOutOfRangeException("arrayIndex");

        if ((array.Length - arrayIndex) < Count)
            throw new ArgumentException(null, "array");

        T[] thisArray = ToArray();
        Array.Copy(thisArray, 0, array, arrayIndex, thisArray.Length);
    }

    bool ICollection<T>.IsReadOnly
    {
        get
        {
            return false;
        }
    }

    bool ICollection<T>.Remove(T item)
    {
        return false;
    }

    private static object _syncObject;
    private static object SyncObject
    {
        get
        {
            if (_syncObject == null)
            {
                object obj = new object();
                Interlocked.CompareExchange(ref _syncObject, obj, null);
            }
            return _syncObject;
        }
    }
}
0 голосов
/ 23 мая 2010
// The following is in C#

public class fqueue
{

  // The following code implements a circular queue of objects

  //private data:

    private bool empty;
    private bool full;

    private int begin, end;

    private object[] x;

  //public data:

    public fqueue()
    {
        empty = !(full = false);
        begin = end = 0xA2;

        x = new object[256];
        return;
    }

    public fqueue(int size)
    {
        if (1 > size) throw new Exception("fqueue: Size cannot be zero or negative");

        empty = !(full = false);
        begin = end = 0xA2;

        x = new object[size];
        return;
    }

    public object write
    {
        set
        {
            if(full) throw new Exception("Write error: Queue is full");

            end = empty ? end : (end + 1) % x.Length;

            full = ((end + 1) % x.Length) == begin;
            empty = false;

            x[end] = value;
        }
    }

    public object read
    {
        get
        {
            if(empty) throw new Exception("Read error: Queue is empty");
            full = false;

            object ret = x[begin];

            begin = (empty=end==begin) ?
                begin :
                (begin + 1) % x.Length;

            return ret;
        }
    }

    public int maxSize
    {
        get
        {
            return x.Length;
        }
    }

    public int queueSize
    {
        get
        {
            return end - begin + (empty ? 0 : 1 + ((end < begin) ? x.Length : 0));
        }
    }

    public bool isEmpty
    {
        get
        {
            return empty;
        }
    }

    public bool isFull
    {
        get
        {
            return full;
        }
    }

    public int start
    {
        get
        {
            return begin;
        }
    }        

    public int finish
    {
        get
        {
            return end;
        }
    }
}
0 голосов
/ 27 февраля 2009
...