Лучший способ объединить два или более байтовых массива в С#

У меня есть 3 байтовых массива в С#, которые мне нужно объединить в один. Каким будет наиболее эффективный метод для выполнения этой задачи?

Ответ 1

Для примитивных типов (включая байты) используйте System.Buffer.BlockCopy вместо System.Array.Copy. Это быстрее.

Я приурочил каждый из предложенных методов в цикле, который выполнялся 1 миллион раз, используя 3 массива по 10 байт каждый. Вот результаты:

  • Новый байт-массив с использованием System.Array.Copy - 0.2187556 секунд
  • Новый массив байтов с использованием System.Buffer.BlockCopy - 0.1406286 секунд
  • IEnumerable <byte> с использованием оператора С# yield - 0.0781270 секунд
  • IEnumerable <byte> с использованием LINQ Concat < > - 0.0781270 секунд

Я увеличил размер каждого массива до 100 элементов и повторил тест:

  • Массив нового байта с использованием System.Array.Copy - 0.2812554 секунд
  • Новый массив байтов с использованием System.Buffer.BlockCopy - 0.2500048 секунд
  • IEnumerable <byte> с использованием оператора С# yield - 0.0625012 секунд
  • IEnumerable <byte> с использованием LINQ Concat < > - 0.0781265 секунд

Я увеличил размер каждого массива до 1000 элементов и повторил тест:

  • Новый массив байтов с использованием System.Array.Copy - 1.0781457 секунд
  • Новый массив байтов с использованием System.Buffer.BlockCopy - 1.0156445 секунд
  • IEnumerable <byte> с использованием оператора С# yield - 0.0625012 секунд
  • IEnumerable <byte> с использованием LINQ Concat < > - 0.0781265 секунд

Наконец, я увеличил размер каждого массива до 1 миллиона элементов и повторно выполнил тест, выполнив каждый цикл только 4000 раз:

  • Новый массив байтов с использованием System.Array.Copy - 13.4533833 секунд
  • Новый массив байтов с использованием System.Buffer.BlockCopy - 13.1096267 секунд
  • IEnumerable <byte> с использованием оператора С# yield - 0 секунд
  • 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 < > . Он только немного медленнее, чем оператор С# 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 <byte> ), я повторно запустил последний тест времени (1 миллион элементов, 4000 итераций), добавив цикл, который итерации полностью массив с каждым проходом:

  • Новый массив байтов с использованием System.Array.Copy - 78.20550510 секунд
  • Новый массив байтов с использованием System.Buffer.BlockCopy - 77.89261900 секунд
  • IEnumerable <byte> с использованием оператора С# yield - 551.7150161 секунд
  • IEnumerable <byte> с использованием LINQ Concat < > - 448.1804799 секунд

Суть в том, что ОЧЕНЬ важно понимать эффективность как создания, так и использования результирующей структуры данных. Простое сосредоточение на эффективности создания может не учитывать неэффективность, связанную с использованием. Кудос, Джон.

Ответ 2

Многие из ответов, как мне кажется, игнорируют указанные требования:

  • Результатом должен быть массив байтов
  • Он должен быть максимально эффективным

Эти два вместе исключают последовательность байтов LINQ - все с yield будет делать невозможным получение окончательного размера без повторения всей последовательности.

Если это не настоящие требования, LINQ может быть идеальным решением (или реализацией IList<T>). Однако я предполагаю, что Superdumbell знает, чего он хочет.

(EDIT: У меня просто была другая мысль: существует большая смысловая разница между копированием массивов и ленивым чтением). Подумайте, что произойдет, если вы измените данные в одном из "исходных" массивов после вызова 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" требует создания массива байтовых массивов, что приводит к дополнительной неэффективности.

Ответ 3

Если вам просто нужен новый массив байтов, используйте следующее:

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, подумайте о том, как использовать оператор вывода С# 2.0:

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

Я привел пример Matt LINQ еще один шаг для чистоты кода:

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

В моем случае массивы небольшие, поэтому меня не касается производительности.

Ответ 5

На самом деле я столкнулся с некоторыми проблемами с использованием Concat... (с массивами в 10 миллионов, он фактически разбился).

Я нашел следующее, чтобы быть простым, легким и работать достаточно хорошо, без сбоев на меня, и он работает для ЛЮБОГО количества массивов (не только трех):

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

Ответ 6

Класс memystream делает эту работу очень красиво для меня. Я не мог заставить класс буфера работать так же быстро, как memystream.

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

Ответ 7

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

Ответ 8

Здесь обобщение ответа, предоставленного @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;
}

Ответ 9

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

Ответ 10

Все, что вам нужно, чтобы передать список байт-массивов, и эта функция вернет вам массив байтов (объединенных). Это лучшее решение, о котором я думаю:).

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

Ответ 11

Конкат - это правильный ответ, но по какой-то причине наибольшее количество голосов получает зарубленная вещь. Если вам нравится этот ответ, возможно, вам понадобится более общее решение:

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