C#当动态数据过大画图有困难时怎么办?

笔者最近写了一个监控软件, 数据量较大, 而所用插件刷新有压力. 所以就写了一个支持数据抽取的大数据维护类.

        // 设计需求:
        // 会发生数据的新增, 并且在数据达到上限时自动去掉前半截数据,
        // 高频的等间隔读取抽有数据,
        // 数据结构大小是可以在生成对象时提供, 并且对不提供大小参数的情况下使用默认大小
        // 数据量较大(大约有40万个元素),
        // 支持等距提取若干个数据,
        //      比如从最后20000个树种提取2000个数据(可通过参数设置这个数量),
        //      提取的规则是:返回提取的数据量, 将提取的数写入到调用者提供的list中

        // 设计思路:
        // 使用List<T> 或LinkedList<T>都有一些弊端:
        // List<T> 在去除数据时,需要将后半部分的数据移动到前面,对于大数据量来说,性能开销较大。
        // LinkedList<T> 在随机访问数据时,效率较低,因为需要从头开始遍历到目标位置。
        // 在C#中,没有现成的数据结构可以同时满足这些需求。因此,自己设计一个数据结构来实现这些功能。
        // 将两个List<T>结合使用,每个List<T>存储一半的数据。当数据达到上限时,丢弃第一个List<T>,并创建一个新的List<T>来存储新的数据。
        public interface IDataExtractor<T>
        {
            (int startIndex, double interval) ExtractDataFromEnd(int lastCount, int extractCount, List<T> targetList);
        }

        public class LargeDataStructure<T> : IDataExtractor<T>
        {
            private const int DefaultHalfMaxRecordsNum = 60 * 60 * 24 * 7 * 2; // Default half of max records
            private int halfMaxRecordsNum;
            private List<T> firstHalf;
            private List<T> secondHalf;
            public int AllDataCount => firstHalf.Count + secondHalf.Count;

            public LargeDataStructure(int? maxRecordsNum = null)
            {
                this.halfMaxRecordsNum = (maxRecordsNum ?? DefaultHalfMaxRecordsNum) / 2;
                this.firstHalf = new List<T>(halfMaxRecordsNum);
                this.secondHalf = new List<T>(halfMaxRecordsNum);
            }

            public void Append(T data)
            {
                if (secondHalf.Count >= halfMaxRecordsNum)
                {
                    // Discard the first half and move the second half to the first
                    firstHalf = secondHalf;
                    secondHalf = new List<T>(halfMaxRecordsNum);
                }

                if (firstHalf.Count < halfMaxRecordsNum)
                {
                    firstHalf.Add(data);
                }
                else
                {
                    secondHalf.Add(data);
                }
            }

            public T this[int index] // indexer
            {
                get
                {
                    if (index < halfMaxRecordsNum)
                    {
                        return firstHalf[index];
                    }
                    else
                    {
                        return secondHalf[index - halfMaxRecordsNum];
                    }
                }
            }

            public (int, double) ExtractDataFromEnd(int lastCount, int extractCount, List<T> targetList)
            {
                targetList.Clear();
                int startIndex = Math.Max(0, AllDataCount - lastCount);
                int validCount = AllDataCount - startIndex;
                double interval = 1;

                if (extractCount >= validCount)
                {
                    // 提取的数量大于等于最后的数据量
                    for (int i = startIndex; i < AllDataCount; i++)
                    {
                        targetList.Add(this[i]);
                    }
                }
                else
                {
                    interval = (double)(validCount-1) / (extractCount - 1); // 计算浮点数等距间隔

                    for (int i = 0; i < extractCount; i++)
                    {
                        int index = (int)Math.Round(startIndex + interval * i);
                        index = Math.Min(index, AllDataCount - 1); // 确保不超过数据范围
                        targetList.Add(this[index]);
                    }
                }
                return (startIndex, interval);
            }
        }
        private void TestExtractDataFromEnd()
        {
            LargeDataStructure<int> dataStructure = new LargeDataStructure<int>();

            // 添加一些数据用于测试
            for (int i = 0; i < 20; i++)
            {
                dataStructure.Append(i);
            }

            List<int> result = new List<int>();

            // 用例1.测试数据量不足,提取 30 个数据
            dataStructure.ExtractDataFromEnd(50, 30, result);
            Console.WriteLine("数据量不足,提取 30 个数据:");
            foreach (int data in result)
            {
                Console.Write(data + " ");
            }
            Console.WriteLine();

            // 再添加一些数据用于测试
            for (int i = 20; i < 100; i++)
            {
                dataStructure.Append(i);
            }


            // 用例2.测试整数等距提取,提取 5 个数据
            dataStructure.ExtractDataFromEnd(10, 5, result);
            Console.WriteLine("整数等距提取(10个数据中提取5个):");
            foreach (int data in result)
            {
                Console.Write(data + " ");
            }
            Console.WriteLine();

            // 用例3.测试浮点数等距提取,提取 20 个数据
            dataStructure.ExtractDataFromEnd(30, 20, result);
            Console.WriteLine("浮点数等距提取(30个数据中提取20个):");
            foreach (int data in result)
            {
                Console.Write(data + " ");
            }
            Console.WriteLine();

            // 用例4.测试尾部数超过需求数, 想从5个数中提取100个(不合法参数
            dataStructure.ExtractDataFromEnd(5, 100, result);
            Console.WriteLine("尾部数超过需求数(5个数据中提取100个):");
            foreach (int data in result)
            {
                Console.Write(data + " ");
            }
            Console.WriteLine();

            Console.ReadLine();
        }

当涉及处理大量数据时,LargeDataStructure类可以作为一个有用的工具。

该类可以存储大量数据,并提供了一些功能来操作和提取这些数据。以下是LargeDataStructure类的总结:

  1. LargeDataStructure类的目的是处理大量数据,它被设计为实现IDataExtractor接口。

  2. LargeDataStructure类内部使用两个List<int>对象(firstHalf和secondHalf)来存储数据。这两个列表分别表示数据的前半部分和后半部分。

  3. 构造函数LargeDataStructure(int? maxRecordsNum = null)允许你创建一个LargeDataStructure对象,并可选地指定最大记录数(maxRecordsNum)。如果未提供最大记录数,将使用默认值。

  4. 方法Append(int data)用于将数据添加到LargeDataStructure对象中。如果secondHalf列表的大小达到最大记录数的一半(halfMaxRecordsNum),则会丢弃firstHalf列表中的数据,并将secondHalf作为新的firstHalf。然后,创建一个新的空的secondHalf列表来接收后续的数据。

  5. LargeDataStructure类还提供了索引器(this[int index]),允许你通过索引访问LargeDataStructure对象中的数据。该索引器考虑了数据的分布,以正确地获取指定索引位置的数据。

  6. 方法ExtractDataFromEnd(int lastCount, int extractCount, List<int> targetList)允许你从LargeDataStructure对象的末尾提取指定数量的数据。你可以指定最后的数据数量(lastCount),要提取的数据数量(extractCount)以及目标列表(targetList)来接收提取的数据。

  7. 如果提取的数量大于等于最后的数据数量,ExtractDataFromEnd方法将提取所有最后的数据。否则,它将根据浮点数等距间隔从数据中提取相应数量的数据。在计算浮点数等距间隔时,会考虑有效数据的数量,以确保提取不超过数据的范围。

LargeDataStructure类提供了一种高效且灵活的方式来处理大量数据,并具有数据添加、数据访问以及从数据末尾提取数据的功能。这个类可以根据你的需求进行进一步的扩展和定制。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C#中,可以使用串口读取数据并绘制图表。根据引用\[1\]中的代码,可以看出在文本方式被选中,通过串口读取数据并将其添加到richTextBox1中。可以使用这些数据来绘制图表。引用\[2\]中的代码展示了串口发送函数,可以根据需要发送数据。 要绘制图表,可以使用C#中的Chart控件。首先,将Chart控件添加到窗体中。然后,可以使用串口读取的数据来更新图表。可以将读取的数据解析为数值,并将其添加到图表的数据系列中。可以使用Chart控件的各种属性和方法来自定义图表的外观和行为。 另一种方法是使用GDI+绘图。根据引用\[3\]中的描述,可以使用GDI+来绘制图表。这种方法需要手动绘制图表的各个元素,但是可以更加灵活地控制图表的绘制过程。可以使用Graphics类的各种方法来绘制线条、矩形、文本等。 无论使用Chart控件还是GDI+绘图,都需要在串口数据接收事件中更新图表。可以在接收到新数据,将其添加到图表中,并重新绘制图表以显示最新的数据。 总之,通过串口读取数据并使用Chart控件或GDI+绘图,可以实现在C#中读取数据并画图的功能。具体的实现方式可以根据需求和个人偏好进行选择和调整。 #### 引用[.reference_title] - *1* *2* *3* [C# 接收串口数据并利用GDI+绘制波形图](https://blog.csdn.net/sanaerqi/article/details/88766020)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值