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



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


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

746   21  
c#

21 ответов:

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

попробуйте это:

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

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

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}

Я остановился на более универсальном решении, которое позволяет объединять произвольный набор одномерных массивов одного и того же типа. (Я объединял 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}

это здесь:

using System.Linq;

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

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

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

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

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

Я знаю, что ОП был только слегка любопытно о производительности. Что большие массивы могут получить другой результат (см. @kurdishTree). И что это обычно не имеет значения (@Иордании.народный.) Тем не менее, мне было любопытно, и поэтому я потерял рассудок ( как объяснял @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

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

будьте осторожны с Concat метод. Пост объединение массивов в C# поясняет, что:

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

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

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

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

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

    }

более эффективно (быстрее) использовать Buffer.BlockCopy over 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 итераций примера в вопросе

функция concat взял 3088ms

копировать, чтобы взял 1079ms

BlockCopy взял 603ms

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

функция concat взял 45945ms

копировать, чтобы взял 2230ms

BlockCopy взял 1689ms

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

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

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

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

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

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

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

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

С помощью LINQ:

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

Я предоставил оба для своих предпочтений. Производительность мудрый @Сергей Штейн или @deepee1 это!--4--> решения немного быстрее, лямбда-выражение является самым медленным. Затраченное время зависит от типа(ов) элементов массива, но если нет миллионов вызовов, нет существенной разницы между методами.

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

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

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

извините, чтобы возродить старую нить, но как насчет этого:

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>[]), чтобы эти функции принимали больше, чем просто массивы.

надеюсь, что это помогает.

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

для меньших массивов

using System.Linq;

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

int[] result = firstArray.ToList().Concat(secondArray.ToList()).toArray();
static class Extensions
{
    public static T[] Concat<T>(this T[] array1, params T[] array2)
    {
        return 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.

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

int[] x = new int [] {1, 2, 3}; int[] y = новый int [] { 4, 5 };

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

Comments

    Ничего не найдено.