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

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

有更简单或更高效的方法吗?


要小心使用Concat方法。在这篇帖子 Array Concatenation in C# 中解释了:

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

对于大型数组来说效率不高。这意味着Concat方法仅适用于中等大小的数组(最多10000个元素)。


8
“efficient”指的是什么?这个代码已经足够简短了,所以我想你指的是CPU/RAM方面的效率? - TToni
4
不,通过Reflector的快速查看可以发现它使用了双倍大小的缓冲区。 - erikkallen
请明确一点,我需要将z定义为int[]类型。 - hwiechers
5
我并不是特别关心效率。(我说过更容易或更有效率的选择。)我提出这个问题是为了了解其他人如何处理这个常见的任务。 - hwiechers
24个回答

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上,请不要仅仅粘贴代码,还要解释您的方法。在这种特定情况下,您可能还需要解释您的晚回答对已经给出(并接受)的答案有何补充。 - Gert Arnold
1
不确定第一个参数前面的“this”是做什么的,但除此之外,这是一个非常优秀的函数。它是通用的,并且可以接受无限数量的参数。 - Nyerguds
2
嗨,Nyerguds。回答你的问题,"this"关键字用于将函数作为扩展方法使用。有关扩展方法的更多信息,请查看此MSDN文章 - JFish222

3
用于保存组合数组的最高效的RAM(和CPU)结构是一种特殊的类,该类实现IEnumerable(或者如果您希望甚至从Array派生)并在内部链接到原始数组以读取值。据我所知,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(...)之前,内存不会被分配,您可以自由地“构建查询”,并通过使用返回yield return t;中的一项来遍历它们所有,也可以通过使用foreach(var i in z){...}子句来遍历它们所有...

以上函数可以制作为以下扩展:

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>[]),以使这些函数接受更多种类的输入,而不仅仅是数组。

希望这能够帮到您。


Thre foreach / yield return is redundant. You could return directly the merged variable. So basicaly your merge is the same as Concat, isn't it? var z = x1.Concat(x2).Contact(x3); za=z.ToArray(); - Liero
@Liero,你没有错,但在这种情况下需要使用foreach/yield return(您可能已经知道,但是这个注释是为未来的读者而写)。当合并大型数据集时,您不希望一次性获取整个数据集(即.ToArray()方法,它可能需要很长时间)。我有var za=x.ToArray()作为示例,通常,合并结果(类型为IEnumerable<T>)需要在循环中进行迭代,以通过每次迭代获得一个结果。 - nurchi
当然,您不需要使用ToArray,但在第一个示例中仍可以(应该)返回'merged'变量,它是IEnumerable类型的。 - Liero
这就是 yield return 的作用(除非我误解了你的评论)。 - nurchi

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 表达式最慢。所需的时间取决于数组元素的类型,但除非有数百万次调用,否则这些方法之间没有明显的区别。

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

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
你需要记住的是,使用LINQ时,你正在利用延迟执行。这里描述的其他方法都可以完美地工作,但它们会立即执行。此外,Concat()函数可能已经被优化了,而你自己无法做到这一点(调用内部API、操作系统调用等)。 总之,除非你真的需要尝试进行优化,否则你目前正在走向“万恶之源”的道路 ;)

1
从C# 12开始,您可以使用集合表达式展开运算符..,例如:
int[] a = [1, 2, 3];
int[] b = [4, 5, 6];
int[] c = [..a, ..b];

注意:虽然我目前对其效率不确定,但它绝对是最简洁和最容易的方法。此外,此功能仅在预览模式下可用,但其正式发布计划于2023年11月。

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
我提出了这个解决方案,因为我认为它总结了其他方案的优点。它是经过精心制作的。 - drowa

0

对于小于10000个元素的数组:

using System.Linq;

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

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

为什么不必要时还要使用Linq呢? - ina

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接