366 Stimmen

Wie kann ich zwei Arrays in C# verketten?

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

Zurzeit verwende ich

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

Gibt es eine einfachere oder effizientere Methode?

14voto

amalgamate Punkte 2070

Ich weiß, dass der Auftraggeber nur ein wenig neugierig auf die Leistung war. Dass größere Arrays ein anderes Ergebnis liefern können (siehe @kurdishTree). Und dass es normalerweise keine Rolle spielt (@jordan.peoples). Nichtsdestotrotz war ich neugierig und habe deshalb den Verstand verloren (wie @TigerShark erklärt hat) .... Ich meine, dass ich einen einfachen Test auf der Grundlage der ursprünglichen Frage.... und aller Antworten.... geschrieben habe.

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

}

Das Ergebnis war:

enter image description here

Würfeln Sie Ihre eigenen Gewinne.

14voto

Jodrell Punkte 32818

Effizienter (schneller) in der Anwendung Buffer.BlockCopy über 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));

Ich habe ein einfaches Testprogramm geschrieben, das den Jitter "aufwärmt", im Release-Modus kompiliert und ohne angeschlossenen Debugger auf meinem Rechner ausgeführt.

Für 10.000.000 Iterationen des in der Frage genannten Beispiels

Concat dauerte 3088ms

CopyTo dauerte 1079ms

BlockCopy dauerte 603ms

Wenn ich die Testfelder auf zwei Sequenzen von 0 bis 99 ändere, erhalte ich ähnliche Ergebnisse wie dieses,

Concat dauerte 45945ms

CopyTo dauerte 2230ms

BlockCopy dauerte 1689ms

Aus diesen Ergebnissen kann ich schließen, dass die CopyTo y BlockCopy Methoden sind wesentlich effizienter als Concat und darüber hinaus, wenn Leistung ein Ziel ist, BlockCopy hat Wert über CopyTo .

Wenn die Leistung keine Rolle spielt oder es nur wenige Iterationen geben wird, sollten Sie die Methode wählen, die Ihnen am leichtesten fällt. Buffer.BlockCopy bietet einen gewissen Nutzen für die Typkonvertierung, der über den Rahmen dieser Frage hinausgeht.

10voto

kurdishTree Punkte 101

Seien Sie vorsichtig mit dem Concat Methode. Die Post Array-Verkettung in C# erklärt das:

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

Wird bei großen Arrays ineffizient sein. Das bedeutet, dass die Concat Methode ist nur für Arrays mittlerer Größe geeignet (bis zu 10000 Elemente).

9voto

Mike Two Punkte 42868

Sie können den ToArray()-Aufruf am Ende weglassen. Gibt es einen Grund, warum Sie es brauchen, um ein Array nach dem Aufruf von Concat sein?

Der Aufruf von Concat erzeugt einen Iterator über beide Arrays. Es wird kein neues Array erstellt, so dass Sie nicht mehr Speicher für ein neues Array verbraucht haben. Wenn Sie ToArray aufrufen, erstellen Sie tatsächlich ein neues Array und nehmen den Speicher für das neue Array in Anspruch.

Wenn Sie also nur einfach über beide iterieren müssen, rufen Sie einfach Concat auf.

8voto

Darkseal Punkte 9244

Hier ist meine Antwort:

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

Diese Methode kann auf der Initialisierungsebene verwendet werden, um beispielsweise eine statische Verkettung von statischen Arrays zu definieren:

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

Es gibt jedoch zwei Vorbehalte, die Sie beachten müssen:

  • En Concat Methode erstellt einen Iterator über beide Arrays: Es wird kein neues Array erstellt, so dass der Speicherplatz effizient genutzt wird: Die anschließende ToArray macht diesen Vorteil zunichte, da es tatsächlich ein neues Array erstellt und den Speicher für das neue Array belegt.
  • Wie @Jodrell sagte, Concat wäre für große Arrays eher ineffizient: Sie sollte nur für mittelgroße Arrays verwendet werden.

Wenn die Leistung ein Muss ist, kann stattdessen die folgende Methode verwendet werden:

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

Oder (für Fans von Einzeilern):

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

Obwohl die letztere Methode viel eleganter ist, ist die erstere definitiv besser für die Leistung.

Weitere Informationen finden Sie unter diese Stelle in meinem Blog.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X