如何在C#中连接两个数组?


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

有没有更简单或更有效的方法?


8
您所说的“高效”是什么意思?代码足够简短,因此,我认为您的意思是在CPU / RAM方面有效吗?
TToni

4
不,对Reflector的快速浏览显示它使用了一个双倍的完整缓冲区
erikkallen,2009年

请注意,我需要z成为int []类型的a。
hwiechers 2009年

4
我并不是真的担心效率。(我确实说过更容易更有效。)我问了一个问题,以检查其他人如何处理这项常见任务。
hwiechers

Answers:


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

8
@manthrax->在防御方面,C#倾向于使用比数组更强大的列表。似乎使用数组的唯一功能目的是用于Interop调用(非托管C ++)。
李维·富勒

@LeviFuller C#使用数组的另一个地方是可变数字params参数。
ChrisW

1
@LeviFuller-奇怪的是,许多系统例程返回数组而不是列表。例如,System.IO.Directory.GetFiles()返回一个字符串数组。
Orion elenzil 2016年

4
这并不奇怪。数组是不可变的,列表不是。同样,列表使用的内存要比数组使用的内存多,除非调用了TrimExcess(在ToList中不会发生)
CSharpie 2016年

1
另外,数组在访问数据时比列表快,因为列表只是将数组包装在内部,并且有调用索引器的开销。
C0DEF52 '18

84

试试这个:

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

5
甚至List<int> list = new List<int>(x);
马修Scharley

7
这比x.Concat(y)更有效吗?一切正常,我只是想知道是否有一些东西可以使它更好?
麦克两点

7
您可能想使第一行List <int> list = new List <int>(x.Length + y.Length); 为了避免在您致电AddRange时可能发生的调整大小
Mike

5
@马修·沙利 问题是寻求更有效的解决方案。我知道标题听起来像任何旧组合都可以,但是完整的问题超出了此范围。阅读一些答案,我只是觉得有些人正在回答标题。因此,我认为这个答案可能值得一提,如果它值得支持,因为这似乎是问题的重点。
迈克两”

2
事实证明,AddRange实际上是一个相当昂贵的过程,因此,此板上的第一个答案应该是首选方法:dotnetperls.com/insertrange
Liam

49

您可以编写一个扩展方法:

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}

1
是否已经没有适用于任何IEnumerable的扩展方法?
迈克两”

2
是的,在大多数情况下,我会很乐意使用它。但是他们有很多开销。这取决于; 98%的时间开销正常。但是,如果您处于2%的水平,那么一些直接的内存复制/数组工作将很方便。
马克·格雷韦尔

1
@nawfal,CopyCopyTo什么更快?关心详细吗?
skrebbel 2012年

1
@skrebbel我的评论不准确。那时我做了一些测试,发现复制速度更快。但是现在看来他们是平等的。那时我可能会发现,总体而言,Marc的方法效率更高,因为他在Zed的方法中创建一个新数组时将同一个实例传递回来。抱歉:)
nawfal 2012年

1
@Shimmy不会。在此方法内部,x只是一个局部变量,将x作为ref传递给resize方法将创建一个新数组,并更改(局部变量)x指向它。或换个说法:传递给resize的x和扩展方法内部的x是相同的变量,但是x不作为引用传递给扩展方法,因此x是与调用此扩展的作用域中的变量不同的变量。
AnorZaken 2015年

40

我决定使用一种更通用的解决方案,该解决方案可以连接任意一组相同类型的一维数组。(我一次要串联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}

功能不错,谢谢!更改params T[][]this T[][]使其扩展。
2012年

嗨,大家好,这个功能看起来像我想要的,但是知道如何实现此功能吗?链接 @Mark
George B

31

就是这个:

using System.Linq;

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

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

4
您的意思是,int[] result = array1.ToList().Concat(array2.ToList()).toArray();我相信您不能直接在阵列上应用Concat
Michail Michailidis 2014年

4
上面的原始问题中提到了z = x.Concat(y)这个解决方案。
乔恩·施耐德

1
没有toArray() Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<string>' to 'string[]'. An explicit conversion exists (are you missing a cast?)
Tibor Udvari,2015年

2
这不是直接的答案。OP要求int[] result = ?,您将答案的问题隐藏在背后var,因为您的结果将IEnumerable<int>不是int[]。(我不喜欢var方法返回的原因之一)
David S.

2
此方法是问题中使用的方法,因此此答案不提供任何新信息,并且如果没有.ToArray()调用,此代码将不会返回实际的数组,因此它也是错误的答案。
Mani Gandham '16

10

您可以结束ToArray()调用。调用Concat之后,是否有理由需要将其作为数组?

调用Concat会在两个数组上创建一个迭代器。它不会创建新阵列,因此您没有为新阵列使用更多的内存。当您调用ToArray时,您实际上确实创建了一个新数组并占用了该新数组的内存。

因此,如果您只需要轻松地迭代两者,则只需致电Concat。


8

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

}

结果是:

在此处输入图片说明

赢得自己的胜利。


为了公平地使用方法,该方法可能在我的系统上增加了大约10,000个刻度。
amalgamate

1
我在发布模式下的Visual Studio 2013中运行了您的代码,发现如果测试的数组不像您的数组那么小(如1000个元素),CopyTo将是最快的,并且比快约3倍Roll your own
Ree先生

@ Mr.Ree是的,我的数组确实很小,不是吗。谢谢。感兴趣的是看看Block副本是否做得更好...
合并

7

使用该Concat方法时要小心。后在C#阵列级联解释说:

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

对于大型阵列将效率低下。这意味着该Concat方法仅适用于中等大小的数组(最多10000个元素)。


2
包含超过10,000个元素的数组应该怎么做?
Alex

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

2
在StackOverflow上,不仅要粘贴代码,还要说明您的方法。在这种特殊情况下,你可能还需要解释一下你迟到的回答增加已经给(和接受)的答案
格特·阿诺德

1
不知道第一个参数之前“ this”在做什么,但是对于其余参数,这是一个出色的功能。通用,具有无限数量的参数。
Nyerguds 2012年

2
嗨Nyerguds。为了回答您的问题,“ this”关键字用于使函数成为扩展方法。有关扩展方法的更多信息,请查看此MSDN文章
JFish222

6

更高效的(快)使用Buffer.BlockCopyArray.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花了3088毫秒

CopyTo花了1079ms

BlockCopy花了603ms

如果我将测试数组更改为从0到99的两个序列,那么我得到的结果与此类似,

Concat花了45945毫秒

CopyTo花了2230ms

BlockCopy花了1689毫秒

从这些结果中,我可以断言CopyToBlockCopy方法比效率要高得多Concat,此外,如果以性能为目标,BlockCopy则价值超过CopyTo

为了解决这个问题,如果性能无关紧要,或者迭代次数很少,请选择最简单的方法。Buffer.BlockCopy确实提供了一些超出此问题范围的实用程序来进行类型转换。


6

迟来的回答:-)。

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

    }

3

就RAM(和CPU)而言,用于保存组合数组的最有效结构是实现IEnumerable(或者如果您希望甚至派生自Array)并在内部链接到原始数组以读取值的特殊类。AFAIK Concat就是这样做的。

在示例代码中,您可以省略.ToArray(),这将使它更有效。


3

很抱歉,无法恢复旧线程,但是如何处理:

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>[]),以使这些函数接受的不仅仅是数组。

希望这可以帮助。


2

您可以按照引用的方式进行操作,或者,如果您想真正地手动使用它,可以滚动自己的循环:

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

@nawfal我认为这将取决于数组大小。这赢得了我的小型阵列测试。
amalgamate 2014年

2

我发现使用LINQLambda表达式的一种优雅的单行解决方案,两者的工作原理相同(编译程序时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@ deepee1的解决方案速度更快,Lambda表达式的速度最慢。花费的时间取决于数组元素的类型,但是除非有数百万个调用,否则这两种方法之间没有显着差异。


1

您需要记住的是,在使用LINQ时,您正在利用延迟执行。这里描述的其他方法都可以正常工作,但是可以立即执行。此外,Concat()函数可能已经以您无法完成的方式(对内部API的调用,对OS的调用等)进行了优化。无论如何,除非您真的需要尝试和优化,否则您目前正处在“万恶之源”的道路上;)


1

尝试以下方法:

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

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

1

这是我的答案:

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

尽管后一种方法要优雅得多,但前一种方法绝对可以提高性能。

有关更多信息,请参阅这篇文章在我的博客。


0

对于int [],您所做的工作对我来说看起来不错。旁听者的答案也适用于List<int>


2
Concat也适用于List <int>。这就是Concat的优点,它可以在任何IEnumerable <>上运行
Mike 2,2009年


0
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。

注意,该解决方案是简洁的,并且所添加的一般性不会增加可观的运行时开销。


我建议您尝试查找较新的问题,或者尚没有很多答案的问题-包括一个几乎与您一样的问题。
Andrew Barber 2014年

我提出此解决方案是因为我认为它总结了其他解决方案的优点。它是精心制作的。
drowa 2014年

-2

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

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


2
Union这不是一个很好的方法,因为它隐式调用Distinct并从联接的集合中删除所有重复项。Concat更好,但这已经在最初的问题中了。
nurchi

-2
int[] scores = { 100, 90, 90, 80, 75, 60 };
int[] alice = { 50, 65, 77, 90, 102 };
int[] scoreBoard = new int[scores.Length + alice.Length];

int j = 0;
for (int i=0;i<(scores.Length+alice.Length);i++)  // to combine two arrays
{
    if(i<scores.Length)
    {
        scoreBoard[i] = scores[i];
    }
    else
    {
        scoreBoard[i] = alice[j];
        j = j + 1;

    }
}


for (int l = 0; l < (scores.Length + alice.Length); l++)
{
    Console.WriteLine(scoreBoard[l]);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.