32

有没有最好的(见下文)在 C# 中附加两个字节数组的方法?

假装我有完全的控制权,我可以使第一个字节数组足够大以在最后容纳第二个字节数组并使用Array.CopyTo函数。或者我可以遍历单个字节并进行分配。

有没有更好的方法?我无法想象像将字节数组转换为字符串并将它们连接起来并将它们转换回来这样的事情会比上述任何一种方法都好。

就最佳/更好(按顺序)而言:

  1. 最快的
  2. 最少的 RAM 消耗

一个限制是我必须在 .NET 2.0 框架中工作。

推荐的两个选择是 MemoryStream 和 BlockCopy。我已经对 10,000,000 个循环进行了 3 次简单的速度测试,得到了以下结果:

3 次运行 10,000,000 次循环的平均值(以毫秒为单位):

  • BlockCopy Time: 1154 , 范围为 13 毫秒
  • MemoryStream GetBuffer 时间:1470,范围为 14 毫秒
  • MemoryStream ToArray Time:1895,范围为3毫秒
  • CopyTo Time:2079,范围为19毫秒
  • 逐字节时间:2203,范围为10毫秒

List<byte> AddRange超过 1000 万次循环的结果:List<byte> 时间:16694

相对 RAM 消耗(1 是基线,越高越差):

  • 逐字节:1
  • 块复制:1
  • 复制到:1
  • 内存流获取缓冲区:2.3
  • MemoryStream ToArray:3.3
  • 列表<字节>:4.2

测试表明,一般来说,除非你正在做大量的字节副本[我是],否则查看字节副本不值得关注[例如,1000 万次运行产生多达 1.1 秒的差异]。

4

8 回答 8

26

You want BlockCopy

According to this blog post it is faster than Array.CopyTo.

于 2009-05-21T21:11:00.387 回答
16

您还可以使用带有 MemoryStream 的方法。假设 b1 和 b2 是两个字节数组,您可以通过以下方式使用 MemoryStream 获得一个新的 b3:

var s = new MemoryStream();
s.Write(b1, 0, b1.Length);
s.Write(b2, 0, b2.Length);
var b3 = s.ToArray();

这应该可以在没有 LINQ 的情况下工作,实际上速度要快得多。

于 2009-05-21T21:29:59.477 回答
11

Create a new MemoryStream passing into the constructor a buffer that's exactly the size of the merged one. Write the individual arrays, and then finally use the buffer:

byte[] deadBeef = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF};
byte[] baadF00d = new byte[] { 0xBA, 0xAD, 0xF0, 0x0D};
int newSize = deadBeef.Length + baadF00d.Length;
var ms = new MemoryStream(new byte[newSize], 0, newSize, true, true);
ms.Write(deadBeef, 0, deadBeef.Length);
ms.Write(baadF00d, 0, baadF00d.Length);
byte[] merged = ms.GetBuffer();

A lot of the low-level I/O functions in .NET take byte arrays and offsets. This was done to prevent needless copies. Be sure you really need the merged array if this is performance sensitive, otherwise just use buffers and offsets.

于 2009-05-21T21:18:36.957 回答
7

Another option, although I haven't tested it to see how it fares in terms of speed and memory consumption, would the LINQ approach:

byte[] combined = bytesOne.Concat(bytesTwo).Concat(bytesThree).ToArray();

...where bytesOne, bytesTwo, and bytesThree are byte arrays. Since Concat uses deferred execution, this shouldn't create any intermediate arrays, and it shouldn't duplicate the original arrays until it constructs the final merged array at the end.

Edit: LINQBridge will allow you to use LINQ-to-Objects (which this is an example of) in the 2.0 framework. I understand if you don't want to depend on this, but it's an option.

于 2009-05-21T21:11:37.483 回答
4

如果您有大小会不时更改的数组,那么您最好List<T>首先使用 a 。然后你可以调用AddRange()列表的方法。

否则,Array.Copy() 或 Array.CopyTo() 与您可能看到的其他任何东西一样好。

于 2009-05-21T21:05:55.437 回答
3

Have you taught about using List or ArrayList instead of an Array? With these types they can grow or shrink and append via InsertRange

于 2009-05-21T21:13:59.727 回答
3

Do you need the output to actually be a byte array?

If not, you could create yourself a "smart cursor" (which is similar to what LINQ does): Create a custom IEnumerator<byte> that will first iterate the first array, and just continue on the second one without interuption.

This would work in the 2.0 framework be fast (in that the joining of arrays has virtually no cost), and use no more RAM than the arrays already consume.

于 2009-05-21T21:22:14.547 回答
1

使第一个数组足够大以包含第二个数组并使用 Array.CopyTo 的第一个选项最终与手动迭代每个项目并进行分配大致相同。Array.CopyTo() 只是让它更简洁。

与上述相比,转换为字符串并返回数组将非常慢。并且可能会使用更多内存。

于 2009-05-21T21:03:17.783 回答