Как объединить два массива в С#?

int[] x = new int [] { 1, 2, 3};
int[] y = new int [] { 4, 5 };

int[] z = // your answer here...

Debug.Assert(z.SequenceEqual(new int[] { 1, 2, 3, 4, 5 }));

-

Сейчас я использую

int[] z = x.Concat(y).ToArray();

Есть ли более простой или эффективный метод?

Ответ 1

var z = new int[x.Length + y.Length];
x.CopyTo(z, 0);
y.CopyTo(z, x.Length);

Ответ 2

Попробуйте следующее:

List<int> list = new List<int>();
list.AddRange(x);
list.AddRange(y);
int[] z = list.ToArray();

Ответ 3

Вы можете написать метод расширения:

public static T[] Concat<T>(this T[] x, T[] y)
{
    if (x == null) throw new ArgumentNullException("x");
    if (y == null) throw new ArgumentNullException("y");
    int oldLen = x.Length;
    Array.Resize<T>(ref x, x.Length + y.Length);
    Array.Copy(y, 0, x, oldLen, y.Length);
    return x;
}

Тогда:

int[] x = {1,2,3}, y = {4,5};
int[] z = x.Concat(y); // {1,2,3,4,5}

Ответ 4

Я остановился на более общем решении, которое позволяет объединить произвольный набор одномерных массивов того же типа. (Я собирал 3+ за раз).

Моя функция:

    public static T[] ConcatArrays<T>(params T[][] list)
    {
        var result = new T[list.Sum(a => a.Length)];
        int offset = 0;
        for (int x = 0; x < list.Length; x++)
        {
            list[x].CopyTo(result, offset);
            offset += list[x].Length;
        }
        return result;
    }

И использование:

        int[] a = new int[] { 1, 2, 3 };
        int[] b = new int[] { 4, 5, 6 };
        int[] c = new int[] { 7, 8 };
        var y = ConcatArrays(a, b, c); //Results in int[] {1,2,3,4,5,6,7,8}

Ответ 5

Это он:

using System.Linq;

int[] array1 = { 1, 3, 5 };
int[] array2 = { 0, 2, 4 };

// Concatenate array1 and array2.
var result1 = array1.Concat(array2);

Ответ 6

Вы можете отменить вызов ToArray(). Есть ли причина, по которой вам нужно, чтобы он был массивом после вызова Concat?

Calling Concat создает итератор по обоим массивам. Он не создает новый массив, поэтому вы не использовали больше памяти для нового массива. Когда вы вызываете ToArray, вы действительно создаете новый массив и занимаете память для нового массива.

Итак, если вам просто нужно легко перебрать и то, и другое, просто назовите Concat.

Ответ 7

Я знаю, что OP только слегка интересовался производительностью. Большие массивы могут получить другой результат (см. @kurdishTree). И это обычно не имеет значения (@jordan.peoples). Тем не менее, мне было любопытно, и поэтому я потерял рассудок (как объяснил @TigerShark).... Я имею в виду, что я написал простой тест, основанный на исходном вопросе.... и все ответы....

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace concat
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] x = new int [] { 1, 2, 3};
            int[] y = new int [] { 4, 5 };


            int itter = 50000;
            Console.WriteLine("test iterations: {0}", itter);

            DateTime startTest = DateTime.Now;
            for(int  i = 0; i < itter; i++)
            {
                int[] z;
                z = x.Concat(y).ToArray();
            }
            Console.WriteLine ("Concat Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks );

            startTest = DateTime.Now;
            for(int  i = 0; i < itter; i++)
            {
                var vz = new int[x.Length + y.Length];
                x.CopyTo(vz, 0);
                y.CopyTo(vz, x.Length);
            }
            Console.WriteLine ("CopyTo Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks );

            startTest = DateTime.Now;
            for(int  i = 0; i < itter; i++)
            {
                List<int> list = new List<int>();
                list.AddRange(x);
                list.AddRange(y);
                int[] z = list.ToArray();
            }
            Console.WriteLine("list.AddRange Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);

            startTest = DateTime.Now;
            for (int i = 0; i < itter; i++)
            {
                int[] z = Methods.Concat(x, y);
            }
            Console.WriteLine("Concat(x, y) Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);

            startTest = DateTime.Now;
            for (int i = 0; i < itter; i++)
            {
                int[] z = Methods.ConcatArrays(x, y);
            }
            Console.WriteLine("ConcatArrays Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);

            startTest = DateTime.Now;
            for (int i = 0; i < itter; i++)
            {
                int[] z = Methods.SSConcat(x, y);
            }
            Console.WriteLine("SSConcat Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);

            startTest = DateTime.Now;
            for (int k = 0; k < itter; k++)
            {
                int[] three = new int[x.Length + y.Length];

                int idx = 0;

                for (int i = 0; i < x.Length; i++)
                    three[idx++] = x[i];
                for (int j = 0; j < y.Length; j++)
                    three[idx++] = y[j];
            }
            Console.WriteLine("Roll your own Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);


            startTest = DateTime.Now;
            for (int i = 0; i < itter; i++)
            {
                int[] z = Methods.ConcatArraysLinq(x, y);
            }
            Console.WriteLine("ConcatArraysLinq Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);

            startTest = DateTime.Now;
            for (int i = 0; i < itter; i++)
            {
                int[] z = Methods.ConcatArraysLambda(x, y);
            }
            Console.WriteLine("ConcatArraysLambda Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);

            startTest = DateTime.Now;
            for (int i = 0; i < itter; i++)
            {
                List<int> targetList = new List<int>(x);
                targetList.Concat(y);
            }
            Console.WriteLine("targetList.Concat(y) Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);

            startTest = DateTime.Now;
            for (int i = 0; i < itter; i++)
            {
                int[] result = x.ToList().Concat(y.ToList()).ToArray();
            }
            Console.WriteLine("x.ToList().Concat(y.ToList()).ToArray() Test Time in ticks: {0}", (DateTime.Now - startTest).Ticks);
        }
    }
    static class Methods
    {
        public static T[] Concat<T>(this T[] x, T[] y)
        {
            if (x == null) throw new ArgumentNullException("x");
            if (y == null) throw new ArgumentNullException("y");
            int oldLen = x.Length;
            Array.Resize<T>(ref x, x.Length + y.Length);
            Array.Copy(y, 0, x, oldLen, y.Length);
            return x;
        }

        public static T[] ConcatArrays<T>(params T[][] list)
        {
            var result = new T[list.Sum(a => a.Length)];
            int offset = 0;
            for (int x = 0; x < list.Length; x++)
            {
                list[x].CopyTo(result, offset);
                offset += list[x].Length;
            }
            return result;
        }


        public static T[] SSConcat<T>(this T[] first, params T[][] arrays)
        {
            int length = first.Length;
            foreach (T[] array in arrays)
            {
                length += array.Length;
            }
            T[] result = new T[length];
            length = first.Length;
            Array.Copy(first, 0, result, 0, first.Length);
            foreach (T[] array in arrays)
            {
                Array.Copy(array, 0, result, length, array.Length);
                length += array.Length;
            }
            return result;
        }

        public static T[] ConcatArraysLinq<T>(params T[][] arrays)
        {
            return (from array in arrays
                    from arr in array
                    select arr).ToArray();
        }

        public static T[] ConcatArraysLambda<T>(params T[][] arrays)
        {
            return arrays.SelectMany(array => array.Select(arr => arr)).ToArray();
        }
    }

}

В результате получилось:

enter image description here

Сверните свои собственные победы.

Ответ 8

Будьте осторожны с методом Concat. Сообщение Конкатенация массива в С# объясняет, что:

var z = x.Concat(y).ToArray();

Будет неэффективно для больших массивов. Это означает, что метод Concat предназначен только для массивов размера meduim (до 10000 элементов).

Ответ 9

public static T[] Concat<T>(this T[] first, params T[][] arrays)
{
    int length = first.Length;
    foreach (T[] array in arrays)
    {
        length += array.Length;
    }
    T[] result = new T[length];
    length = first.Length;
    Array.Copy(first, 0, result, 0, first.Length);
    foreach (T[] array in arrays)
    {
        Array.Copy(array, 0, result, length, array.Length);
        length += array.Length;
    }
    return result;
}

Ответ 10

Поздний ответ: -).

public static class ArrayExtention
    {

        public static T[] Concatenate<T>(this T[] array1, T[] array2)
        {
            T[] result = new T[array1.Length + array2.Length];
            array1.CopyTo(result, 0);
            array2.CopyTo(result, array1.Length);
            return result;
        }

    }

Ответ 11

Более эффективный (быстрее) использовать Buffer.BlockCopy над Array.CopyTo,

int[] x = new int [] { 1, 2, 3};
int[] y = new int [] { 4, 5 };

int[] z = new int[x.Length + y.Length];
var byteIndex = x.Length * sizeof(int);
Buffer.BlockCopy(x, 0, z, 0, byteIndex);
Buffer.BlockCopy(y, 0, z, byteIndex, y.Length * sizeof(int));

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

Для 10 000 000 итераций примера в вопросе

Конкат занял 3088 м.

CopyTo взял 1079ms

BlockCopy занял 603 мс

Если я изменяю тестовые массивы на две последовательности от 0 до 99, то получаю аналогичные результаты,

Конкат занял 45945мс

CopyTo взял 2230 мс

BlockCopy занял 1689 мс

Из этих результатов я могу утверждать, что методы CopyTo и BlockCopy значительно эффективнее, чем Concat, и, кроме того, если производительность является целью, BlockCopy имеет значение над CopyTo.

Чтобы отказать в ответе, если производительность не имеет значения, или будет несколько итераций, выберите метод, который вы найдете наиболее простым. Buffer.BlockCopy предлагает некоторую полезность для преобразования типов за пределы этого вопроса.

Ответ 12

Вы можете сделать это так, как вы упомянули, или если вы хотите получить действительно руководство по этому поводу, вы можете перевернуть свой собственный цикл:

        string[] one = new string[] { "a", "b" };
        string[] two = new string[] { "c", "d" };
        string[] three;

        three = new string[one.Length + two.Length];

        int idx = 0;

        for (int i = 0; i < one.Length; i++)
            three[idx++] = one[i];
        for (int j = 0; j < two.Length; j++)
            three[idx++] = two[j];

Ответ 13

Самая эффективная структура с точки зрения ОЗУ (и ЦП) для хранения объединенного массива будет специальным классом, который реализует IEnumerable (или, если вы хотите, даже выводится из массива), и связывается внутри с исходными массивами, чтобы прочитать значения. AFAIK Concat делает именно это.

В вашем примере кода вы можете опустить .ToArray(), хотя это сделает его более эффективным.

Ответ 14

Что нужно помнить, так это то, что при использовании LINQ вы используете отложенное выполнение. Другие описанные здесь методы работают отлично, но они выполняются немедленно. Кроме того, функция Concat(), вероятно, оптимизирована таким образом, что вы не можете сделать сами (вызовы внутренних API, вызовы OS и т.д.). В любом случае, если вам действительно не нужно пытаться оптимизировать, вы в настоящее время находитесь на своем пути к "корню всех злых";)

Ответ 15

Я нашел элегантное однострочное решение, используя LINQ или Lambda, оба работают одинаково (LINQ преобразуется в Lambda при компиляции программы). Решение работает для любого типа массива и для любого количества массивов.

Использование LINQ:

public static T[] ConcatArraysLinq<T>(params T[][] arrays)
{
    return (from array in arrays
            from arr in array
            select arr).ToArray();
}

Использование Lambda:

public static T[] ConcatArraysLambda<T>(params T[][] arrays)
{
    return arrays.SelectMany(array => array.Select(arr => arr)).ToArray();
}

Я предоставил оба варианта. Эффективные решения @Sergey Shteyn's или @deepee1 немного быстрее, причем выражение Lambda является самым медленным. Принятое время зависит от типа (ов) элементов массива, но если не существует миллионов вызовов, между этими методами нет существенной разницы.

Ответ 16

Попробуйте следующее:

T[] r1 = new T[size1];
T[] r2 = new T[size2];

List<T> targetList = new List<T>(r1);
targetList.Concat(r2);
T[] targetArray = targetList.ToArray();

Ответ 17

Извините, что возродил старый поток, но как насчет этого:

static IEnumerable<T> Merge<T>(params T[][] arrays)
{
    var merged = arrays.SelectMany(arr => arr);

    foreach (var t in merged)
        yield return t;
}

Затем в вашем коде:

int[] x={1, 2, 3};
int[] y={4, 5, 6};

var z=Merge(x, y);  // 'z' is IEnumerable<T>

var za=z.ToArray(); // 'za' is int[]

Пока вы не назовете .ToArray(), .ToList() или .ToDictionary(...), память не будет выделена, вы можете "построить свой запрос" и либо вызвать один из этих трех для ее выполнения, либо просто пройти через все их используя предложение foreach (var i in z){...}, которое возвращает элемент за раз от yield return t; выше...

Вышеупомянутая функция может быть сделана в расширение следующим образом:

static IEnumerable<T> Merge<T>(this T[] array1, T[] array2)
{
    var merged = array1.Concat(array2);

    foreach (var t in merged)
        yield return t;
}

Итак, в коде вы можете сделать что-то вроде:

int[] x1={1, 2, 3};
int[] x2={4, 5, 6};
int[] x3={7, 8};

var z=x1.Merge(x2).Merge(x3);   // 'z' is IEnumerable<T>

var za=z.ToArray(); // 'za' is int[]

Остальное будет таким же, как раньше.

Другим улучшением этого будет изменение T[] на IEnumerable<T> (поэтому params T[][] станет params IEnumerable<T>[]), чтобы эти функции принимали больше, чем просто массивы.

Надеюсь, что это поможет.

Ответ 18

Вот мой ответ:

int[] z = new List<string>()
    .Concat(a)
    .Concat(b)
    .Concat(c)
    .ToArray();

Этот метод может использоваться на уровне инициализации, например, для определения статической конкатенации статических массивов:

public static int[] a = new int [] { 1, 2, 3, 4, 5 };
public static int[] b = new int [] { 6, 7, 8 };
public static int[] c = new int [] { 9, 10 };

public static int[] z = new List<string>()
    .Concat(a)
    .Concat(b)
    .Concat(c)
    .ToArray();

Тем не менее, он поставляется с двумя оговорками, которые необходимо учитывать:

  • Метод Concat создает итератор для обоих массивов: он не создает новый массив, что делает его эффективным с точки зрения используемой памяти: однако последующий ToArray нет такое преимущество, поскольку он фактически создаст новый массив и займет память для нового массива.
  • Как сказал @Jodrell, Concat будет довольно неэффективен для больших массивов: его следует использовать только для массивов среднего размера.

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

/// <summary>
/// Concatenates two or more arrays into a single one.
/// </summary>
public static T[] Concat<T>(params T[][] arrays)
{
    // return (from array in arrays from arr in array select arr).ToArray();

    var result = new T[arrays.Sum(a => a.Length)];
    int offset = 0;
    for (int x = 0; x < arrays.Length; x++)
    {
        arrays[x].CopyTo(result, offset);
        offset += arrays[x].Length;
    }
    return result;
}

Или (для однолинейных фанатов):

int[] z = (from arrays in new[] { a, b, c } from arr in arrays select arr).ToArray();

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

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

Ответ 19

Для int [] то, что вы сделали, выглядит хорошо для меня. Ответ будет хорошо работать для List<int>.

Ответ 20

Для меньших массивов и 10000 элементов:

using System.Linq;

int firstArray = {5,4,2};
int secondArray = {3,2,1};

int[] result = firstArray.ToList().Concat(secondArray.ToList()).toArray();

Ответ 21

Я нашел это самым простым, учитывая, что массивы идентичны, конечно:

array3 = array1.Union(array2).ToArray();

Ответ 22

static class Extensions
{
    public static T[] Concat<T>(this T[] array1, params T[] array2) => ConcatArray(array1, array2);

    public static T[] ConcatArray<T>(params T[][] arrays)
    {
        int l, i;

        for (l = i = 0; i < arrays.Length; l += arrays[i].Length, i++);

        var a = new T[l];

        for (l = i = 0; i < arrays.Length; l += arrays[i].Length, i++)
            arrays[i].CopyTo(a, l);

        return a;
    }
}

Я думаю, что вышеупомянутое решение является более общим и более легким, чем другие, которые я видел здесь. Он носит более общий характер, поскольку не ограничивает объединение только для двух массивов, и легче, поскольку не использует ни LINQ, ни List.

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

Ответ 23

int [] x = new int [] {1, 2, 3}; int [] y = new int [] {4, 5};

int [] z = x.Union(y).ToArray();