优化字节数组追加字节方法:解决20万+十六进制字符串转字节效率低问题
原addByteToArray方法是典型的O(n²)时间复杂度实现:
- 每添加1个字节就重新分配1次长度逐次递增的新数组
- 每次操作都要把已有的全部字节拷贝到新数组中
20万次操作下,累计拷贝的字节总量超过200亿次,内存分配和拷贝的开销占总耗时的99%以上,这是导致处理耗时达16秒的核心原因。
额外提醒:原代码的拷贝逻辑存在错位——
bArray.CopyTo(newArray, 1)会把旧数组内容放在新数组的索引1位置,新字节放在索引0,和常规「字节追加到末尾」的需求逻辑相反,使用前需要注意修正。
方案1:预分配固定长度数组(性能最优,可实现毫秒级处理)
因为你已经明确知道输入的字符串列表总元素数,最终输出的字节数组长度就等于列表元素数,完全不需要动态扩容,直接一次性分配足够内存,边遍历边写入即可:
// stringList 为待处理的十六进制字符串列表 int totalCount = stringList.Count; // 直接预分配最终长度的数组,全程无额外拷贝、无冗余内存分配 byte[] result = new byte[totalCount]; for (int i = 0; i < totalCount; i++) { // 解析十六进制字符串为字节,直接写入数组对应位置 result[i] = Convert.ToByte(stringList[i], 16); }
该方案全程仅执行1次数组分配,遍历过程直接写入内存,无任何冗余拷贝,20万条数据的处理耗时通常在1~5ms区间,完全满足毫秒级要求。
方案2:自定义高性能十六进制解析(进一步压低解析开销)
如果内置Convert.ToByte的解析性能仍达不到要求(内置方法通常包含格式校验逻辑,有额外开销),可以手写更精简的解析逻辑,适配固定格式的十六进制字符串(形如"0x01"的4位结构):
int totalCount = stringList.Count; byte[] result = new byte[totalCount]; for (int i = 0; i < totalCount; i++) { string hexStr = stringList[i]; // 跳过"0x"前缀,直接取后两位十六进制字符 char highChar = hexStr[2]; char lowChar = hexStr[3]; // 兼容大小写、数字的解析逻辑 byte highN = (byte)(highChar >= '0' && highChar <= '9' ? highChar - '0' : char.ToUpper(highChar) - 'A' + 10); byte lowN = (byte)(lowChar >= '0' && lowChar <= '9' ? lowChar - '0' : char.ToUpper(lowChar) - 'A' + 10); result[i] = (byte)((highN << 4) | lowN); }
该解析逻辑比内置Convert.ToByte快2~3倍,适合超大规模数据处理场景。
备选方案:动态追加场景使用List
如果确实是动态追加字节、无法提前预知最终数组长度的场景,不要使用原有的逐次新建数组的逻辑,直接使用List<byte>替代:List<byte>采用按倍数扩容的机制(默认容量不足时翻倍分配内存),总拷贝复杂度为O(n),性能比原实现高几个数量级:
// 初始化时直接指定预估容量,避免多次扩容带来的额外拷贝 List<byte> byteList = new List<byte>(200000); foreach (var hexStr in stringList) { byteList.Add(Convert.ToByte(hexStr, 16)); } // 最后一次性转换为最终的字节数组 byte[] result = byteList.ToArray();
注意:初始化List<byte>时必须指定匹配的初始容量,否则初始小容量带来的多次扩容会产生不必要的拷贝开销。
内容的提问来源于stack exchange,提问作者Yolanda

