zoukankan      html  css  js  c++  java
  • 【转】C#环形队列

    概述

    看了一个数据结构的教程,是用C++写的,可自己C#还是一个菜鸟,更别说C++了,但还是大胆尝试用C#将其中的环形队列的实现写出来,先上代码:

    复制代码
     1     public class MyQueue<T> : IDisposable
     2     {
     3         private T[] queue;
     4         private int length;
     5         private int capacity;
     6         private int head = 0;
     7         private int tail = 0;
     8 
     9         public MyQueue(int capacity) {
    10             this.capacity = capacity;
    11             this.head = 0;
    12             this.tail = 0;
    13             this.length = 0;
    14             this.queue = new T[capacity];
    15         }
    16 
    17         public void Clear() {
    18             head = 0;
    19             tail = 0;
    20             length = 0;
    21         }
    22 
    23         public bool IsEmpty() {
    24             return length == 0;
    25         }
    26 
    27         public bool IsFull() {
    28             return length == capacity;
    29         }
    30 
    31         public int Length() {
    32             return length;
    33         }
    34 
    35         public bool EnQueue(T node) {
    36             if (!IsFull()) {
    37                 queue[tail] = node;
    38                 tail = (++tail) % capacity;
    39                 length++;
    40                 return true;
    41             }
    42             return false;
    43         }
    44 
    45         public T DeQueue() {
    46             T node = default(T);
    47             if (!IsEmpty()) {
    48                 node = queue[head];
    49                 head = (++head) % capacity;
    50                 length--;
    51             }
    52             return node;
    53         }
    54 
    55         public void Traverse() {
    56             for (int i = head; i < length + head; i++) {
    57                 Console.WriteLine(queue[i % capacity]);
    58                 Console.WriteLine($"前面还有{i - head}个");
    59             }
    60         }
    61 
    62         public void Dispose() {
    63             queue = null;
    64         }
    65     }
    复制代码

    为了能够通用,所以用的是泛型来实现环形队列类。这里最重要的是进队(EnQueue)和出队(DeQueue)两个方法,进队或出队后头和尾的位置都要通过取模运算来获得,因为是环形队列嘛,你懂的。

    一、简单类型队列

    好了,测试下入队:
    复制代码
     1     class Program
     2     {
     3         static void Main(string[] args) {
     4             MyQueue<int> queue = new MyQueue<int>(4);
     5             queue.EnQueue(10);
     6             queue.EnQueue(16);
     7             queue.EnQueue(18);
     8             queue.EnQueue(12);
     9             queue.Traverse();
    10             Console.Read();
    11         }
    12     }
    复制代码
    显示结果:

    再测试下出队:
    复制代码
     1     class Program
     2     {
     3         static void Main(string[] args) {
     4             MyQueue<int> queue = new MyQueue<int>(4);
     5             queue.EnQueue(10);
     6             queue.EnQueue(16);
     7             queue.EnQueue(18);
     8             queue.EnQueue(12);
     9             queue.Traverse();
    10 
    11             Console.WriteLine("弹两个出去");
    12             queue.DeQueue();
    13             queue.DeQueue();
    14             Console.WriteLine();
    15             queue.Traverse();
    16             Console.Read();
    17         }
    18     }
    复制代码

    运行结果:

     

    二、复杂类型队列

    之前也说了,这个队列类是用的泛型写的,对应于C++的模板了,那就意味着任何类型都可以使用这个队列类,来测试个自定义的类试试,如下先定义一个Customer类:
    复制代码
     1     public class Customer
     2     {
     3         public string Name { get; set; }
     4 
     5         public int Age { get; set; }
     6 
     7         public void PringInfo() {
     8             Console.WriteLine("姓名:" + Name);
     9             Console.WriteLine("年龄:" + Age);
    10             Console.WriteLine();
    11         }
    12     }
    复制代码

    然后进行入队,如下:

    复制代码
     1     class Program
     2     {
     3         static void Main(string[] args) {
     4             MyQueue<Customer> queue = new MyQueue<Customer>(5);
     5             queue.EnQueue(new Customer() { Name = "宋小二", Age = 29 });
     6             queue.EnQueue(new Customer() { Name = "陈小三", Age = 28 });
     7             queue.EnQueue(new Customer() { Name = "王小四", Age = 26 });
     8             queue.EnQueue(new Customer() { Name = "朱小五", Age = 48 });
     9             for (int i = 0; i < queue.Length(); i++) {
    10                 queue[i].PringInfo();
    11             }
    12             Console.Read();
    13         }
    14     }
    复制代码

    上面的代码 queue[i].PringInfo();是通过索引来实现,所以我们得在队列类中实现索引,添加如下代码到MyQueue.cs类中,如下:

    1         public T this[int index] {
    2             get {
    3                 return queue[index];
    4             }
    5         }

    感觉用for循环来遍历还是不够好,想用foreach,那就给MyQueue类加个遍历接口,如下:

    然后实现这个接口,如下:
    复制代码
     1         public IEnumerator<T> GetEnumerator() {
     2             foreach(T node in queue) {
     3                 if(node != null) { 
     4                     yield return node;
     5                 }
     6             }
     7         }
     8 
     9         IEnumerator IEnumerable.GetEnumerator() {
    10             return GetEnumerator();
    11         }
    复制代码

    这样遍历的地方就可以改成foreach了,如下:

    执行结果:
     
     
    文2:

    公司项目中经常设计到串口通信,TCP通信,而且大多都是实时的大数据的传输,然后大家都知道协议通讯肯定涉及到什么,封包、拆包、粘包、校验……什么鬼的概念一大堆,说简单点儿就是要一个高效率可复用的缓存区。按照码农的惯性思维就是去百度、谷歌搜索看有没有现成的东西可以直接拿来用,然而我并没有找到,好吧不是很难的东西自己实现一个呗。开扯……

    为什么要用环形队列?

    环形队列是在实际编程极为有用的数据结构,它有如下特点:

    它是一个首尾相连的FIFO的数据结构,采用数组的线性空间,数据组织简单。能很快知道队列是否满为空。能以很快速度的来存取数据。

    因为有简单高效的原因,甚至在硬件都实现了环形队列。

    C#完全实现(可直接使用)

    鄙人新手这份代码肯定有不足之处,望大家指出交流,涉及到的多线程同步问题请调用者完成,不废话直接上代码。

    public class RingBufferManager
    {
        public byte[] Buffer { get; set; } // 存放内存的数组
        public int DataCount { get; set; } // 写入数据大小
        public int DataStart { get; set; } // 数据起始索引
        public int DataEnd { get; set; }   // 数据结束索引
        public RingBufferManager(int bufferSize)
        {
            DataCount = 0; DataStart = 0; DataEnd = 0;
            Buffer = new byte[bufferSize];
        }
    
        public byte this[int index]
        {
            get
            {
                if (index >= DataCount) throw new Exception("环形缓冲区异常,索引溢出");
                if (DataStart + index < Buffer.Length)
                {
                    return Buffer[DataStart + index];
                }
                else 
                {
                    return Buffer[(DataStart + index) - Buffer.Length];
                }
            }
        }
    
        public int GetDataCount() // 获得当前写入的字节数
        {
            return DataCount;
        }
    
        public int GetReserveCount() // 获得剩余的字节数
        {
            return Buffer.Length - DataCount;
        }
    
        public void Clear()
        {
            DataCount = 0;
        }
    
        public void Clear(int count) // 清空指定大小的数据
        {
            if (count >= DataCount) // 如果需要清理的数据大于现有数据大小,则全部清理
            {
                DataCount = 0;
                DataStart = 0;
                DataEnd = 0;
            }
            else
            {
                if (DataStart + count >= Buffer.Length)
                {
                    DataStart = (DataStart + count) - Buffer.Length;
                }
                else 
                {
                    DataStart += count;
                }
                DataCount -= count;
            }
        }
    
        public void WriteBuffer(byte[] buffer, int offset, int count)
        {
            Int32 reserveCount = Buffer.Length - DataCount;
            if (reserveCount >= count)                          // 可用空间够使用
            {
                if (DataEnd + count < Buffer.Length)            // 数据没到结尾
                {
                    Array.Copy(buffer, offset, Buffer, DataEnd, count);
                    DataEnd += count;
                    DataCount += count;
                }
                else           //  数据结束索引超出结尾 循环到开始
                {
                    System.Diagnostics.Debug.WriteLine("缓存重新开始....");
                    Int32 overflowIndexLength = (DataEnd + count) - Buffer.Length;      // 超出索引长度
                    Int32 endPushIndexLength = count - overflowIndexLength;             // 填充在末尾的数据长度
                    Array.Copy(buffer, offset, Buffer, DataEnd, endPushIndexLength);
                    DataEnd = 0;
                    offset += endPushIndexLength;
                    DataCount += endPushIndexLength;
                    if (overflowIndexLength != 0)
                    {
                        Array.Copy(buffer, offset, Buffer, DataEnd, overflowIndexLength);
                    }
                    DataEnd += overflowIndexLength;                                     // 结束索引
                    DataCount += overflowIndexLength;                                   // 缓存大小
                }
            }
            else 
            {
                // 缓存溢出,不处理
            }
        }
    
        public void ReadBuffer(byte[] targetBytes,Int32 offset, Int32 count) 
        {
            if (count > DataCount) throw new Exception("环形缓冲区异常,读取长度大于数据长度");
            Int32 tempDataStart = DataStart;
            if (DataStart + count < Buffer.Length)
            {
                Array.Copy(Buffer, DataStart, targetBytes, offset, count);
            }
            else 
            {
                Int32 overflowIndexLength = (DataStart + count) - Buffer.Length;    // 超出索引长度
                Int32 endPushIndexLength = count - overflowIndexLength;             // 填充在末尾的数据长度
                Array.Copy(Buffer, DataStart, targetBytes, offset, endPushIndexLength);
                
                offset += endPushIndexLength;
                
                if (overflowIndexLength != 0)
                {
                    Array.Copy(Buffer, 0, targetBytes, offset, overflowIndexLength);
                }
            }
        }
    
    
        public void WriteBuffer(byte[] buffer)
        {
            WriteBuffer(buffer, 0, buffer.Length);
        }
    
    }
        

    调用实例

    生产

    int len = sConn.Receive(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, out se);
    if (len <= 0) throw new Exception("disconnect..");
    if (len > 0)
    {
        lock (LockReceiveBuffer)
        {
            while (len + receiveBufferManager.DataCount > MAX_BUFFER_LEN)       // 缓存溢出处理
            {
                Monitor.Wait(LockReceiveBuffer,10000);
            }
            receiveBufferManager.WriteBuffer(receiveBuffer, 0, len);
            Monitor.PulseAll(LockReceiveBuffer);
        }
    }

    消费

    lock (LockReceiveBuffer)
    {
        freame_byte = new byte[frameLen];
        receiveBufferManager.ReadBuffer(freame_byte, 0, frameLen);
        receiveBufferManager.Clear(frameLen);
    }
  • 相关阅读:
    Jedis常用方法 java
    Redis的set、map、list、value、实体类java
    Spring RedisTemplate关闭坏连接
    linux启动vue
    Mybatis实现批量更新sql语句(SSM实现批量更新sql语句)
    mybatis执行批量更新batch update 的方法(oracle,mysql)
    【VM+win7】VM虚拟机安装win7系统教程https://blog.csdn.net/hyhui13/article/details/82682181
    【beyond compare 4】秘钥过期解决办法
    【laravel5】Carbon类
    【Redis+PHP】利用redis的zset实现游戏排行榜功能
  • 原文地址:https://www.cnblogs.com/toloe/p/5728048.html
Copyright © 2011-2022 走看看