2008-08-04 11 views
49

Tôi có một đối tượng Queue <T> mà tôi đã khởi tạo với dung lượng 2, nhưng rõ ràng đó chỉ là dung lượng và nó tiếp tục mở rộng khi tôi thêm các mục. Đã có một đối tượng tự động giải phóng một mục khi đạt đến giới hạn hay là giải pháp tốt nhất để tạo lớp được thừa hưởng của riêng tôi?Kích thước giới hạn của Hàng đợi <T> trong .NET?

Trả lời

31

Tôi đã loại bỏ một phiên bản cơ bản của những gì tôi đang tìm kiếm, nó không hoàn hảo nhưng nó sẽ làm công việc cho đến khi một cái gì đó tốt hơn đi kèm.

public class LimitedQueue<T> : Queue<T> 
{ 
    public int Limit { get; set; } 

    public LimitedQueue(int limit) : base(limit) 
    { 
     Limit = limit; 
    } 

    public new void Enqueue(T item) 
    { 
     while (Count >= Limit) 
     { 
      Dequeue(); 
     } 
     base.Enqueue(item); 
    } 
} 
+0

tôi tăng cường mã hơi với một cuộc gọi từ bên trong Set tài sản Hạn chế để đảm bảo rằng kích thước Queue đã không vượt quá Giới hạn - chỉ đơn giản Trong khi lớn hơn Giới hạn, Dequeue. Ngoài ra, đây là một giải pháp tuyệt vời thats tốt đẹp và đơn giản, cảm ơn. – Scott

+0

Tiếp nhận tốt khi thay đổi mã 'setter' cho thuộc tính 'Limit'. –

+17

Có một giới hạn rất nghiêm trọng đối với lớp này, mà Marcus Griep gợi ý trong câu trả lời của anh ta: vì phương thức 'Enqueue' của bạn được khai báo là' new' (vì 'Queue .Enqueue' không phải là ảo), nếu ai đó bỏ' LimitedQueue của bạn 'vào một' Queue 'họ sẽ có thể thêm bao nhiêu mục tùy thích mà không có giới hạn của bạn. Tôi cũng khuyên bạn nên thay đổi 'if (this.Count> = this.Limit)' thành 'while (this.Count> = this.Limit)', chỉ để ở bên an toàn (đối với kịch bản mà tôi vừa đề cập, ví dụ). –

3

Tại sao bạn không sử dụng mảng có kích thước 2? Hàng đợi được cho là có khả năng phát triển và thu nhỏ một cách linh hoạt.

Hoặc tạo lớp bao bọc xung quanh phiên bản Queue<T> bản sao và mỗi lần một đối tượng là một đối tượng <T>, hãy kiểm tra kích thước của hàng đợi. Nếu lớn hơn 2, dequeue mục đầu tiên.

5

Bạn nên tạo lớp học của riêng bạn, một chiếc nhẫn có lẽ sẽ phù hợp với nhu cầu của bạn.

Cấu trúc dữ liệu trong .NET cho phép bạn chỉ định dung lượng, ngoại trừ mảng, sử dụng công cụ này để xây dựng cấu trúc dữ liệu nội bộ được sử dụng để giữ dữ liệu nội bộ.

Ví dụ, đối với một danh sách, dung lượng được sử dụng để định kích thước một mảng nội bộ. Khi bạn bắt đầu thêm các phần tử vào danh sách, nó sẽ bắt đầu lấp đầy mảng này từ chỉ mục 0 trở lên và khi nó đạt đến dung lượng của bạn, nó sẽ tăng dung lượng lên một dung lượng mới cao hơn và tiếp tục lấp đầy nó.

15

Tôi khuyên bạn nên kéo lên C5 Library. Không giống như SCG (System.Collections.Generic), C5 được lập trình để giao diện và được thiết kế để được phân lớp. Hầu hết các phương thức công khai là ảo và không có lớp nào được niêm phong. Bằng cách này, bạn sẽ không phải sử dụng từ khóa "mới" đó sẽ không kích hoạt nếu số LimitedQueue<T> của bạn được truyền đến SCG.Queue<T>. Với C5 và sử dụng gần mã giống như bạn đã có trước đây, bạn có thể lấy được từ số CircularQueue<T>. Các CircularQueue<T> thực sự thực hiện cả một ngăn xếp và một hàng đợi, vì vậy bạn có thể nhận được cả hai tùy chọn với một giới hạn gần như miễn phí. Tôi đã viết lại nó bên dưới với một số cấu trúc 3,5:

using C5; 

public class LimitedQueue<T> : CircularQueue<T> 
{ 
    public int Limit { get; set; } 

    public LimitedQueue(int limit) : base(limit) 
    { 
     this.Limit = limit; 
    } 

    public override void Push(T item) 
    { 
     CheckLimit(false); 
     base.Push(item); 
    } 

    public override void Enqueue(T item) 
    { 
     CheckLimit(true); 
     base.Enqueue(item); 
    } 

    protected virtual void CheckLimit(bool enqueue) 
    { 
     while (this.Count >= this.Limit) 
     { 
      if (enqueue) 
      { 
       this.Dequeue(); 
      } 
      else 
      { 
       this.Pop(); 
      } 
     } 
    } 
} 

Tôi nghĩ rằng mã này nên làm chính xác những gì bạn đang tìm kiếm.

3

Tôi hy vọng lớp học này sẽ giúp bạn:
Nội bộ Bộ đệm FIFO tròn sử dụng Hàng đợi <T> với kích thước được chỉ định. Khi đạt đến kích thước của bộ đệm, nó sẽ thay thế các mục cũ hơn bằng các mục mới.

LƯU Ý: Bạn không thể xóa các mục một cách ngẫu nhiên. Tôi đặt phương thức Remove (T item) để trả về false. nếu bạn muốn, bạn có thể sửa đổi để loại bỏ các mục ngẫu nhiên

public class CircularFIFO<T> : ICollection<T> , IDisposable 
{ 
    public Queue<T> CircularBuffer; 

    /// <summary> 
    /// The default initial capacity. 
    /// </summary> 
    private int capacity = 32; 

    /// <summary> 
    /// Gets the actual capacity of the FIFO. 
    /// </summary> 
    public int Capacity 
    { 
     get { return capacity; }   
    } 

    /// <summary> 
    /// Initialize a new instance of FIFO class that is empty and has the default initial capacity. 
    /// </summary> 
    public CircularFIFO() 
    {    
     CircularBuffer = new Queue<T>(); 
    } 

    /// <summary> 
    /// Initialize a new instance of FIFO class that is empty and has the specified initial capacity. 
    /// </summary> 
    /// <param name="size"> Initial capacity of the FIFO. </param> 
    public CircularFIFO(int size) 
    { 
     capacity = size; 
     CircularBuffer = new Queue<T>(capacity); 
    } 

    /// <summary> 
    /// Adds an item to the end of the FIFO. 
    /// </summary> 
    /// <param name="item"> The item to add to the end of the FIFO. </param> 
    public void Add(T item) 
    { 
     if (this.Count >= this.Capacity) 
      Remove(); 

     CircularBuffer.Enqueue(item); 
    } 

    /// <summary> 
    /// Adds array of items to the end of the FIFO. 
    /// </summary> 
    /// <param name="item"> The array of items to add to the end of the FIFO. </param> 
    public void Add(T[] item) 
    { 
     int enqueuedSize = 0; 
     int remainEnqueueSize = this.Capacity - this.Count; 

     for (; (enqueuedSize < item.Length && enqueuedSize < remainEnqueueSize); enqueuedSize++) 
      CircularBuffer.Enqueue(item[enqueuedSize]); 

     if ((item.Length - enqueuedSize) != 0) 
     { 
      Remove((item.Length - enqueuedSize));//remaining item size 

      for (; enqueuedSize < item.Length; enqueuedSize++) 
       CircularBuffer.Enqueue(item[enqueuedSize]); 
     }   
    } 

    /// <summary> 
    /// Removes and Returns an item from the FIFO. 
    /// </summary> 
    /// <returns> Item removed. </returns> 
    public T Remove() 
    { 
     T removedItem = CircularBuffer.Peek(); 
     CircularBuffer.Dequeue(); 

     return removedItem; 
    } 

    /// <summary> 
    /// Removes and Returns the array of items form the FIFO. 
    /// </summary> 
    /// <param name="size"> The size of item to be removed from the FIFO. </param> 
    /// <returns> Removed array of items </returns> 
    public T[] Remove(int size) 
    { 
     if (size > CircularBuffer.Count) 
      size = CircularBuffer.Count; 

     T[] removedItems = new T[size]; 

     for (int i = 0; i < size; i++) 
     { 
      removedItems[i] = CircularBuffer.Peek(); 
      CircularBuffer.Dequeue(); 
     } 

     return removedItems; 
    } 

    /// <summary> 
    /// Returns the item at the beginning of the FIFO with out removing it. 
    /// </summary> 
    /// <returns> Item Peeked. </returns> 
    public T Peek() 
    { 
     return CircularBuffer.Peek(); 
    } 

    /// <summary> 
    /// Returns the array of item at the beginning of the FIFO with out removing it. 
    /// </summary> 
    /// <param name="size"> The size of the array items. </param> 
    /// <returns> Array of peeked items. </returns> 
    public T[] Peek(int size) 
    { 
     T[] arrayItems = new T[CircularBuffer.Count]; 
     CircularBuffer.CopyTo(arrayItems, 0); 

     if (size > CircularBuffer.Count) 
      size = CircularBuffer.Count; 

     T[] peekedItems = new T[size]; 

     Array.Copy(arrayItems, 0, peekedItems, 0, size); 

     return peekedItems; 
    } 

    /// <summary> 
    /// Gets the actual number of items presented in the FIFO. 
    /// </summary> 
    public int Count 
    { 
     get 
     { 
      return CircularBuffer.Count; 
     } 
    } 

    /// <summary> 
    /// Removes all the contents of the FIFO. 
    /// </summary> 
    public void Clear() 
    { 
     CircularBuffer.Clear(); 
    } 

    /// <summary> 
    /// Resets and Initialize the instance of FIFO class that is empty and has the default initial capacity. 
    /// </summary> 
    public void Reset() 
    { 
     Dispose(); 
     CircularBuffer = new Queue<T>(capacity); 
    } 

    #region ICollection<T> Members 

    /// <summary> 
    /// Determines whether an element is in the FIFO. 
    /// </summary> 
    /// <param name="item"> The item to locate in the FIFO. </param> 
    /// <returns></returns> 
    public bool Contains(T item) 
    { 
     return CircularBuffer.Contains(item); 
    } 

    /// <summary> 
    /// Copies the FIFO elements to an existing one-dimensional array. 
    /// </summary> 
    /// <param name="array"> The one-dimensional array that have at list a size of the FIFO </param> 
    /// <param name="arrayIndex"></param> 
    public void CopyTo(T[] array, int arrayIndex) 
    { 
     if (array.Length >= CircularBuffer.Count) 
      CircularBuffer.CopyTo(array, 0);   
    } 

    public bool IsReadOnly 
    { 
     get { return false; } 
    } 

    public bool Remove(T item) 
    { 
     return false; 
    } 

    #endregion 

    #region IEnumerable<T> Members 

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

    #endregion 

    #region IEnumerable Members 

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

    #endregion 

    #region IDisposable Members 

    /// <summary> 
    /// Releases all the resource used by the FIFO. 
    /// </summary> 
    public void Dispose() 
    {   
     CircularBuffer.Clear(); 
     CircularBuffer = null; 
     GC.Collect(); 
    } 

    #endregion 
} 
+1

tôi nghĩ rằng bằng cách sử dụng mã này bạn có thể có một hàng đợi kích thước giới hạn .. đó cũng là bộ đệm Thông tư. –

1

Nếu đó là của bất kỳ sử dụng cho bất cứ ai, tôi đã LimitedStack<T>.

public class LimitedStack<T> 
{ 
    public readonly int Limit; 
    private readonly List<T> _stack; 

    public LimitedStack(int limit = 32) 
    { 
     Limit = limit; 
     _stack = new List<T>(limit); 
    } 

    public void Push(T item) 
    { 
     if (_stack.Count == Limit) _stack.RemoveAt(0); 
     _stack.Add(item); 
    } 

    public T Peek() 
    { 
     return _stack[_stack.Count - 1]; 
    } 

    public void Pop() 
    { 
     _stack.RemoveAt(_stack.Count - 1); 
    } 

    public int Count 
    { 
     get { return _stack.Count; } 
    } 
} 

Loại bỏ mục cũ nhất (dưới cùng của ngăn xếp) khi nó quá lớn.

(Câu hỏi này là kết quả nhất trên Google "C# giới hạn ngăn xếp kích thước")

+0

Mã này đúng 99%. Tuy nhiên, nếu chúng ta gọi Peek hoặc Pop mà không đặt bất cứ thứ gì lên ngăn xếp, nó sẽ sụp đổ khi chỉ mục là -1. Điều này có thể dễ dàng cố định bằng cách thêm kiểm tra giới hạn chỉ mục. – Contango

+0

Đề xuất thêm các mục sau vào Peek và Pop(): \t \t \t nếu ((_stack.Count - 1) <0) ném ngoại lệ mới ("Không thể Peek hoặc Pop mà không cần nhấn lần đầu.") ;. Điều này sẽ cảnh báo cho lập trình viên về trường hợp góc này và cho phép họ ghi nhớ khi sử dụng lớp này. Chúng tôi cũng có thể thêm một TryPeek hoặc TryPop, đó là phương pháp mà Microsoft đã thực hiện với triển khai ConcurrentDictionary của họ. – Contango

+1

Đối với hồ sơ, mã này không phải là chủ đề an toàn mà không cần khóa bổ sung (điều này là hoàn toàn tốt, an toàn luồng không bao giờ là một phần của thông số kỹ thuật thiết kế cho lớp này). – Contango