在C#中组合两个或多个字节数组的最佳方法

我有3个字节的数组,我需要合并成一个。 什么是完成这个任务最有效的方法?

对于原始types(包括字节),使用System.Buffer.BlockCopy而不是System.Array.Copy 。 速度更快

我使用3个10字节的数组对每个build议的方法进行循环执行100万次。 结果如下:

  1. 新的字节数组使用System.Array.Copy – 0.2187556秒
  2. 新的字节数组使用System.Buffer.BlockCopy – 0.1406286秒
  3. IEnumerable <byte>使用C#yield运算符 – 0.0781270秒
  4. IEnumerable <byte>使用LINQ的Concat <> – 0.0781270秒

我将每个数组的大小增加到100个元素,然后重新运行testing:

  1. 新的字节数组使用System.Array.Copy – 0.2812554秒
  2. 新的字节数组使用System.Buffer.BlockCopy – 0.2500048秒
  3. IEnumerable <byte>使用C#yield运算符 – 0.0625012秒
  4. IEnumerable <byte>使用LINQ的Concat <> – 0.0781265秒

我将每个数组的大小增加到1000个元素,然后重新运行testing:

  1. 新的字节数组使用System.Array.Copy – 1.0781457秒
  2. 新的字节数组使用System.Buffer.BlockCopy – 1.0156445秒
  3. IEnumerable <byte>使用C#yield运算符 – 0.0625012秒
  4. IEnumerable <byte>使用LINQ的Concat <> – 0.0781265秒

最后,我将每个数组的大小增加到100万个元素,并重新运行testing,每个循环执行4000次:

  1. 新的字节数组使用System.Array.Copy – 13.4533833秒
  2. 新的字节数组使用System.Buffer.BlockCopy – 13.1096267秒
  3. IEnumerable <byte>使用C#yield运算符 – 0秒
  4. IEnumerable <byte>使用LINQ的Concat <> – 0秒

所以,如果你需要一个新的字节数组,使用

 byte[] rv = new byte[a1.Length + a2.Length + a3.Length]; System.Buffer.BlockCopy(a1, 0, rv, 0, a1.Length); System.Buffer.BlockCopy(a2, 0, rv, a1.Length, a2.Length); System.Buffer.BlockCopy(a3, 0, rv, a1.Length + a2.Length, a3.Length); 

但是,如果您可以使用IEnumerable<byte> ,则DEFINITELY更喜欢LINQ的Concat <>方法。 它只比C#屈服运算符稍慢,但更简洁,更优雅。

 IEnumerable<byte> rv = a1.Concat(a2).Concat(a3); 

如果您有任意数量的数组并且正在使用.NET 3.5,则可以使System.Buffer.BlockCopy解决scheme更通用,如下所示:

 private byte[] Combine(params byte[][] arrays) { byte[] rv = new byte[arrays.Sum(a => a.Length)]; int offset = 0; foreach (byte[] array in arrays) { System.Buffer.BlockCopy(array, 0, rv, offset, array.Length); offset += array.Length; } return rv; } 

*注意:上面的块需要在顶部添加以下名称空间才能工作。

 using System.Linq; 

对于Jon Skeet关于迭代后续数据结构(字节数组与IEnumerable <byte>)的观点,我重新运行了最后的时序testing(100万个元素,4000次迭代),添加了一个循环遍历整个数组通过:

  1. 新的字节数组使用System.Array.Copy – 78.20550510秒
  2. 新的字节数组使用System.Buffer.BlockCopy – 77.89261900秒
  3. IEnumerable <byte>使用C#yield运算符 – 551.7150161秒
  4. IEnumerable <byte>使用LINQ的Concat <> – 448.1804799秒

重点是,理解所产生的数据结构的创build和使用的效率是非常重要的。 只关注创造的效率可能会忽视与使用有关的低效率。 荣誉,乔恩

许多答案似乎是忽略了规定的要求:

  • 结果应该是一个字节数组
  • 它应该尽可能高效

这两者一起排除了一个LINQ序列的字节 – 任何带有yield东西都将使得不通过整个序列迭代就得到最终的大小。

如果这些不是真正的要求,那么LINQ可能是一个非常好的解决scheme(或IList<T>实现)。 不过,我会假设Superdumbell知道他想要什么。

(编辑:我刚刚有另一个想法,做一个数组副本和懒惰地阅读之间有一个很大的语义差异考虑如果你改变数据在一个“源”数组后调用Combine (或不pipe用什么方法,但是在使用结果之前 – 懒惰的评估,这种变化将是可见的,使用立即拷贝不会,不同的情况会调用不同的行为 – 只是需要注意的事情。

这里是我提出的方法 – 与其他一些答案中包含的方法非常相似,当然:)

 public static byte[] Combine(byte[] first, byte[] second) { byte[] ret = new byte[first.Length + second.Length]; Buffer.BlockCopy(first, 0, ret, 0, first.Length); Buffer.BlockCopy(second, 0, ret, first.Length, second.Length); return ret; } public static byte[] Combine(byte[] first, byte[] second, byte[] third) { byte[] ret = new byte[first.Length + second.Length + third.Length]; Buffer.BlockCopy(first, 0, ret, 0, first.Length); Buffer.BlockCopy(second, 0, ret, first.Length, second.Length); Buffer.BlockCopy(third, 0, ret, first.Length + second.Length, third.Length); return ret; } public static byte[] Combine(params byte[][] arrays) { byte[] ret = new byte[arrays.Sum(x => x.Length)]; int offset = 0; foreach (byte[] data in arrays) { Buffer.BlockCopy(data, 0, ret, offset, data.Length); offset += data.Length; } return ret; } 

当然,“params”版本要求首先创build一个字节数组,这会带来额外的低效率。

如果你只是需要一个新的字节数组,然后使用以下内容:

 byte[] Combine(byte[] a1, byte[] a2, byte[] a3) { byte[] ret = new byte[a1.Length + a2.Length + a3.Length]; Array.Copy(a1, 0, ret, 0, a1.Length); Array.Copy(a2, 0, ret, a1.Length, a2.Length); Array.Copy(a3, 0, ret, a1.Length + a2.Length, a3.Length); return ret; } 

或者,如果您只需要一个IEnumerable,请考虑使用C#2.0 yield运算符:

 IEnumerable<byte> Combine(byte[] a1, byte[] a2, byte[] a3) { foreach (byte b in a1) yield return b; foreach (byte b in a2) yield return b; foreach (byte b in a3) yield return b; } 

我把Matt的LINQ例子更进了一步,用于代码清洁:

 byte[] rv = a1.Concat(a2).Concat(a3).ToArray(); 

在我的情况下,数组很小,所以我不关心性能。

我实际上遇到了一些使用Concat的问题…(数组中有1000万个数组,实际上是崩溃的)。

我发现以下是简单,容易和工作得很好,而不会崩溃对我来说,它适用于任何数量的数组(不只是三个):

 public static byte[] ConcatByteArrays(params byte[][] arrays) { return arrays.SelectMany(x => x).ToArray(); } 

memorystream类对我来说这个工作非常好。 我无法让缓冲区类运行得像内存stream一样快。

 using (MemoryStream ms = new MemoryStream()) { ms.Write(BitConverter.GetBytes(22),0,4); ms.Write(BitConverter.GetBytes(44),0,4); ms.ToArray(); } 

以下是@Jon Skeet提供的答案的概括。 它基本上是一样的,只有它可用于任何types的数组,不仅字节:

 public static T[] Combine<T>(T[] first, T[] second) { T[] ret = new T[first.Length + second.Length]; Buffer.BlockCopy(first, 0, ret, 0, first.Length); Buffer.BlockCopy(second, 0, ret, first.Length, second.Length); return ret; } public static T[] Combine<T>(T[] first, T[] second, T[] third) { T[] ret = new T[first.Length + second.Length + third.Length]; Buffer.BlockCopy(first, 0, ret, 0, first.Length); Buffer.BlockCopy(second, 0, ret, first.Length, second.Length); Buffer.BlockCopy(third, 0, ret, first.Length + second.Length, third.Length); return ret; } public static T[] Combine<T>(params T[][] arrays) { T[] ret = new T[arrays.Sum(x => x.Length)]; int offset = 0; foreach (T[] data in arrays) { Buffer.BlockCopy(data, 0, ret, offset, data.Length); offset += data.Length; } return ret; } 
  public static bool MyConcat<T>(ref T[] base_arr, ref T[] add_arr) { try { int base_size = base_arr.Length; int size_T = System.Runtime.InteropServices.Marshal.SizeOf(base_arr[0]); Array.Resize(ref base_arr, base_size + add_arr.Length); Buffer.BlockCopy(add_arr, 0, base_arr, base_size * size_T, add_arr.Length * size_T); } catch (IndexOutOfRangeException ioor) { MessageBox.Show(ioor.Message); return false; } return true; } 
  public static byte[] Concat(params byte[][] arrays) { using (var mem = new MemoryStream(arrays.Sum(a => a.Length))) { foreach (var array in arrays) { mem.Write(array, 0, array.Length); } return mem.ToArray(); } } 

所有你需要传递字节数组的列表,这个函数将返回字节数组(合并)。 这是我认为最好的解决scheme:)。

 public static byte[] CombineMultipleByteArrays(List<byte[]> lstByteArray) { using (var ms = new MemoryStream()) { using (var doc = new iTextSharp.text.Document()) { using (var copy = new PdfSmartCopy(doc, ms)) { doc.Open(); foreach (var p in lstByteArray) { using (var reader = new PdfReader(p)) { copy.AddDocument(reader); } } doc.Close(); } } return ms.ToArray(); } } 

Concat是正确的答案,但由于某种原因,一个手卷的东西得到最多的票。 如果你喜欢这个答案,也许你会更喜欢这个更通用的解决scheme:

  IEnumerable<byte> Combine(params byte[][] arrays) { foreach (byte[] a in arrays) foreach (byte b in a) yield return b; } 

这会让你做这样的事情:

  byte[] c = Combine(new byte[] { 0, 1, 2 }, new byte[] { 3, 4, 5 }).ToArray();