如何在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();
有一个更容易或更有效的方法?
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}
我决定了一个更通用的解决scheme,它允许连接任意一组相同types的一维数组。 (我每次连接3+。)
我的function:
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()调用closures。 在Concat调用之后,你是否需要它作为数组?
调用Concat在两个数组上创build一个迭代器。 它不会创build一个新的数组,所以你没有使用更多的内存用于新的数组。 当你调用ToArray时,你实际上会创build一个新的数组,并占用新数组的内存。
所以,如果你只需要轻松地遍历这两个,那么就调用Concat。
我知道OP对性能只有轻微的好奇。 较大的数组可能会得到不同的结果(请参阅@kurdishTree)。 而这通常并不重要(@ jordan.peoples)。 无论如何,我很好奇,因此迷失了我的想法(正如@TigerShark所解释的)….我的意思是我根据原始问题和所有的答案写了一个简单的testing….
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(); } } }
结果是:
滚动自己的胜利。
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; }
Concat
方法要小心。 C#中的数组串联解释说明:
var z = x.Concat(y).ToArray();
对于大型数组将会效率低下。 这意味着Concat
方法只适用于中等大小的数组(最多10000个元素)。
通过Array.CopyTo
使用Buffer.BlockCopy
更高效(更快)
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));
我编写了一个简单的testing程序,用于“加热抖动”,在发布模式下编译并在我的机器上运行,而不用附加debugging器。
对于问题中的示例的10,000,000次迭代
Concat花了3088ms
CopyTo花了1079ms
BlockCopy花了603ms
如果我将testing数组更改为两个从0到99的序列,那么我得到的结果与此类似,
Concat花了45945ms
CopyTo需要2230ms
BlockCopy花了1689ms
从这些结果我可以断言, CopyTo
和BlockCopy
方法比Concat
更有效,而且,如果性能是目标, BlockCopy
比CopyTo
更有价值。
要回答这个问题,如果性能不重要,或者迭代次数less,则select最简单的方法。 Buffer.BlockCopy
确实提供了超出此问题范围的types转换的一些实用程序。
你可以按照你所提到的方式来做,或者如果你想得到真正的手册,你可以推出你自己的循环:
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];
用于存放组合数组的RAM(和CPU)最有效的结构是一个特殊的类,它实现了IEnumerable(或者你甚至希望从数组派生出来),并且在内部链接到原始数组来读取值。 AFAIK Concat就是这么做的。
在你的示例代码中,你可以省略.ToArray(),但这会提高效率。
迟到的答案:-)。
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; } }
你需要记住的是,当使用LINQ时,你正在使用延迟执行。 这里描述的其他方法都可以很好地工作,但是它们会立即执行。 此外,Concat()函数可能会被优化,你不能自己做(调用内部的API,OS调用等)。 无论如何,除非你真的需要尝试和优化,你现在正走上“万恶之源”的道路;)
我发现了一个使用LINQ或Lambdaexpression式的优雅的单行解决scheme,两者工作方式相同(编译程序时LINQ转换为Lambda)。 该解决scheme适用于任何数组types和任何数量的数组。
使用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的解决scheme有点快,Lambdaexpression式是最慢的。 所花费的时间取决于数组元素的types,但除非有数以百万计的调用,否则这些方法之间没有显着差异。
尝试以下操作:
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(...)
,内存不分配,你可以自由地“build立你的查询”,或者调用其中之一来执行它或简单地去通过它们全部使用foreach (var i in z){...}
子句,它从yield return t;
一次返回一个item yield return t;
以上…
上述function可以做如下扩展:
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 []你做了什么看起来不错。 旁观者的答案也适用于List<int>
。
对于较小的数组<10000个元素:
using System.Linq; int firstArray = {5,4,2}; int secondArray = {3,2,1}; int[] result = firstArray.ToList().Concat(secondArray.ToList()).toArray();
我发现这是最简单的,因为arrays是相同的,当然:
array3 = array1.Union(array2).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; } }
我认为上述解决scheme比我在这里看到的其他解决scheme更普遍和更轻。 这是更一般的,因为它不限制只有两个数组的连接,并且更轻,因为它不使用LINQ和List。
注意解决scheme是简洁的,增加的通用性不会增加显着的运行时间开销。
int [] x = new int [] {1,2,3}; int [] y = new int [] {4,5};
int [] z = x.Union(y).ToArray();