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

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

Это строгое правило, если вы пишете публичный API или любой другой код, который будет использоваться многими людьми. Вы можете игнорировать это правило, если у вас есть крошечное приложение и не более 2 разработчиков. Это сэкономит вам время.

Для маленьких приложений вы также можете выбрать другой, менее строгий язык. Ruby, JavaScript - все, что позволяет вам писать меньше кода.

230
задан casperOne 26 May 2011 в 06:49
поделиться

5 ответов

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

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

  1. Новый Массив байтов с помощью System.Array.Copy - 0,2187556 секунды
  2. Новый Массив байтов с помощью System.Buffer.BlockCopy - 0,1406286 секунды
  3. IEnumerable< байт> использующий C# приводит к оператору - 0,0781270 секунды
  4. IEnumerable< байт> использование Concat< LINQ;> - 0,0781270 секунды

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

  1. Новый Массив байтов с помощью System.Array.Copy - 0,2812554 секунды
  2. Новый Массив байтов с помощью System.Buffer.BlockCopy - 0,2500048 секунды
  3. IEnumerable< байт> использующий C# приводит к оператору - 0,0625012 секунды
  4. IEnumerable< байт> использование Concat< LINQ;> - 0,0781265 секунды

я увеличил размер каждого массива к 1 000 элементов и повторно выполнил тест:

  1. Новый Массив байтов с помощью [1 110] - 1,0781457 секунды
  2. Новый Массив байтов с помощью [1 111] - 1,0156445 секунды
  3. IEnumerable< байт> использующий C# приводит к оператору - 0,0625012 секунды
  4. IEnumerable< байт> использование Concat< LINQ;> - 0,0781265 секунды

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

  1. Новый Массив байтов с помощью [1 112] - 13,4533833 секунд
  2. Новый Массив байтов с помощью [1 113] - 13,1096267 секунд
  3. IEnumerable< байт> использующий C# приводит к оператору - 0 секунд
  4. IEnumerable< байт> использование Concat< LINQ;> - 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>, ОПРЕДЕЛЕННО , предпочитают Concat< LINQ;> метод. Это незначительно медленнее, чем оператор урожая C#, но более кратко и более изящно.

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

*Note: вышеупомянутый блок требует, чтобы Вы добавляющий следующее пространство имен в вершина для него работали.

using System.Linq;

К точке Jon Skeet относительно повторения последующих структур данных (массив байтов по сравнению с IEnumerable< байт>), я повторно выполнил последний тест синхронизации (1 миллион элементов, 4 000 повторений), добавив цикл, который выполняет итерации по полному массиву с каждой передачей:

  1. Новый Массив байтов с помощью [1 116] - 78,20550510 секунд
  2. Новый Массив байтов с помощью [1 117] - 77,89261900 секунд
  3. IEnumerable< байт> использующий C# приводит к оператору - 551,7150161 секунды
  4. IEnumerable< байт> использование Concat< LINQ;> - 448,1804799 секунд

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

318
ответ дан Cristian Ciupitu 23 November 2019 в 03:41
поделиться

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

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

, Эти два вместе исключают последовательность 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" версия требует создания массива массивов байтов сначала, который представляет дополнительную неэффективность.

146
ответ дан Jon Skeet 23 November 2019 в 03:41
поделиться

При простой необходимости в новом массиве байтов то используйте следующее:

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:

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;
}
27
ответ дан FryGuy 23 November 2019 в 03:41
поделиться

Concat является правильным ответом, но по некоторым причинам handrolled вещь получает большинство голосов. Если бы Вам нравится этот ответ, возможно, Вы хотели бы это более общее решение еще больше:

    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();
-5
ответ дан Mark Maxham 23 November 2019 в 03:41
поделиться

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

using (MemoryStream ms = new MemoryStream())
{
  ms.Write(BitConverter.GetBytes(22),0,4);
  ms.Write(BitConverter.GetBytes(44),0,4);
  ms.ToArray();
}
6
ответ дан 23 November 2019 в 03:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: