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

时间:2009-01-06 02:54:34

标签: c# arrays

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

13 个答案:

答案 0 :(得分:305)

对于基本类型(包括字节),请使用System.Buffer.BlockCopy而不是System.Array.Copy。它更快。

我使用3个10字节的数组,在一个循环中执行了100万次的每个建议方法。结果如下:

  1. 使用System.Array.Copy的新字节数组 - 0.2187556秒
  2. 使用System.Buffer.BlockCopy的新字节数组 - 0.1406286秒
  3. 的IEnumerable<字节>使用C#yield operator - 0.0781270秒
  4. 的IEnumerable<字节>使用LINQ的Concat<> - 0.0781270秒
  5. 我将每个数组的大小增加到100个元素并重新运行测试:

    1. 使用System.Array.Copy的新字节数组 - 0.2812554秒
    2. 使用System.Buffer.BlockCopy - 0.2500048秒
    3. 的新字节数组
    4. 的IEnumerable<字节>使用C#yield operator - 0.0625012秒
    5. 的IEnumerable<字节>使用LINQ的Concat<> - 0.0781265秒
    6. 我将每个数组的大小增加到1000个元素并重新运行测试:

      1. 使用System.Array.Copy的新字节数组 - 1.0781457秒
      2. 使用System.Buffer.BlockCopy的新字节数组 - 1.0156445秒
      3. 的IEnumerable<字节>使用C#yield operator - 0.0625012秒
      4. 的IEnumerable<字节>使用LINQ的Concat<> - 0.0781265秒
      5. 最后,我将每个数组的大小增加到100万个元素并重新运行测试,执行每个循环 4000次:

        1. 使用System.Array.Copy的新字节数组 - 13.4533833秒
        2. 使用System.Buffer.BlockCopy的新字节数组 - 13.1096267秒
        3. 的IEnumerable<字节>使用C#yield operator - 0秒
        4. 的IEnumerable<字节>使用LINQ的Concat<> - 0秒
        5. 因此,如果您需要一个新的字节数组,请使用

          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> 绝对 更喜欢LINQ的Concat&lt;&gt;方法。它只比C#yield运算符略慢,但更简洁,更优雅。

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

          如果你有任意数量的数组并使用.NET 3.5,你可以使System.Buffer.BlockCopy解决方案更通用,如下所示:

          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&lt; byte&gt;)的迭代的观点,我重新运行了最后的定时测试(100万个元素,4000次迭代),添加了一个遍历完整循环的循环每次传递的数组:

          1. 使用System.Array.Copy的新字节数组 - 78.20550510秒
          2. 使用System.Buffer.BlockCopy的新字节数组 - 77.89261900秒
          3. 的IEnumerable&LT;字节&GT;使用C#yield operator - 551.7150161秒
          4. 的IEnumerable&LT;字节&GT;使用LINQ的Concat&lt;&gt; - 448.1804799秒
          5. 重点是, 非常 对于理解结果数据结构的创建和使用的效率非常重要。仅仅关注创建的效率可能会忽略与使用相关的低效率。 Kudos,Jon。

答案 1 :(得分:137)

在我看来,许多答案都忽略了规定的要求:

  • 结果应为字节数组
  • 应该尽可能高效

这两个一起排除了LINQ字节序列 - 任何带有yield的东西都会使得无法在不迭代整个序列的情况下获得最终大小。

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

(编辑:我刚才有另外一个想法。制作数组副本和懒惰地阅读它们之间存在很大的语义差异。考虑一下如果在调用之后更改其中一个“源”数组中的数据会发生什么Combine(或其他)方法,但在使用结果之前 - 使用延迟评估,该更改将是可见的。使用立即复制,它不会。不同的情况将要求不同的行为 - 只需注意一些事情。)

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

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”版本需要首先创建一个字节数组的数组,这会带来额外的低效率。

答案 2 :(得分:33)

我将Matt的LINQ示例更进一步用于代码清洁:

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

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

答案 3 :(得分:27)

如果您只需要一个新的字节数组,请使用以下命令:

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;
}

答案 4 :(得分:9)

我实际上遇到了使用Concat的一些问题...(在1000万的数组中,它实际上已经崩溃了)。

我发现以下内容简单,容易并且运行良好而不会崩溃,它适用于任意数量的数组(不只是三个)(它使用LINQ):

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

答案 5 :(得分:5)

memorystream类对我来说非常好。我无法让缓冲类以与内存流一样快的速度运行。

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

答案 6 :(得分:2)

    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;
    }

答案 7 :(得分:2)

    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();
        }
    }

答案 8 :(得分:1)

可以使用泛型来组合数组。以下代码可以轻松扩展为三个数组。这样,您无需为不同类型的数组重复代码。以上某些答案对我来说似乎过于复杂。

private static T[] CombineTwoArrays<T>(T[] a1, T[] a2)
    {
        T[] arrayCombined = new T[a1.Length + a2.Length];
        Array.Copy(a1, 0, arrayCombined, 0, a1.Length);
        Array.Copy(a2, 0, arrayCombined, a1.Length, a2.Length);
        return arrayCombined;
    }

答案 9 :(得分:0)

这是@Jon Skeet提供的答案的概括。 它基本上是相同的,只有它可用于任何类型的数组,而不仅仅是字节:

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;
}

答案 10 :(得分:0)

    /// <summary>
    /// Combine two Arrays with offset and count
    /// </summary>
    /// <param name="src1"></param>
    /// <param name="offset1"></param>
    /// <param name="count1"></param>
    /// <param name="src2"></param>
    /// <param name="offset2"></param>
    /// <param name="count2"></param>
    /// <returns></returns>
    public static T[] Combine<T>(this T[] src1, int offset1, int count1, T[] src2, int offset2, int count2) 
        => Enumerable.Range(0, count1 + count2).Select(a => (a < count1) ? src1[offset1 + a] : src2[offset2 + a - count1]).ToArray();

答案 11 :(得分:-1)

您需要传递Byte Arrays列表,此函数将返回Bytes数组(Merged)。 这是我认为的最佳解决方案:)。

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();
            }
        }

答案 12 :(得分:-5)

康卡特是正确的答案,但出于某种原因,手动的东西获得的选票最多。如果你喜欢这个答案,也许你会更喜欢这个更通用的解决方案:

    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();