How do I concatenate two arrays in 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 }));
Right now I use
int[] z = x.Concat(y).ToArray();
Is there an easier or more efficient method?
Be careful with the Concat
method. The post Array Concatenation in C# explains that:
var z = x.Concat(y).ToArray();
Will be inefficient for large arrays. That means the Concat
method is only for meduim-siz开发者_StackOverflow社区ed arrays (up to 10000 elements).
var z = new int[x.Length + y.Length];
x.CopyTo(z, 0);
y.CopyTo(z, x.Length);
Try this:
List<int> list = new List<int>();
list.AddRange(x);
list.AddRange(y);
int[] z = list.ToArray();
You could write an extension method:
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;
}
Then:
int[] x = {1,2,3}, y = {4,5};
int[] z = x.Concat(y); // {1,2,3,4,5}
This is it:
using System.Linq;
int[] array1 = { 1, 3, 5 };
int[] array2 = { 0, 2, 4 };
// Concatenate array1 and array2.
int[] result1 = array1.Concat(array2).ToArray();
I settled on a more general-purpose solution that allows concatenating an arbitrary set of one-dimensional arrays of the same type. (I was concatenating 3+ at a time.)
My 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;
}
And usage:
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}
I know the OP was only mildly curious about performance. That larger arrays may get a different result (see @kurdishTree). And that it usually does not matter (@jordan.peoples). None the less, I was curious and therefore lost my mind ( as @TigerShark was explaining).... I mean that I wrote a simple test based on the original question.... and all the answers....
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();
}
}
}
The result was:
Roll your own wins.
More efficient (faster) to use 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));
I wrote a simple test program that "warms up the Jitter", compiled in release mode and ran it without a debugger attached, on my machine.
For 10,000,000 iterations of the example in the question
Concat took 3088ms
CopyTo took 1079ms
BlockCopy took 603ms
If I alter the test arrays to two sequences from 0 to 99 then I get results similar to this,
Concat took 45945ms
CopyTo took 2230ms
BlockCopy took 1689ms
From these results I can assert that the CopyTo
and BlockCopy
methods are significantly more efficient than Concat
and furthermore, if performance is a goal, BlockCopy
has value over CopyTo
.
To caveat this answer, if performance doesn't matter, or there will be few iterations choose the method you find easiest. Buffer.BlockCopy
does offer some utility for type conversion beyond the scope of this question.
You can take the ToArray() call off the end. Is there a reason you need it to be an array after the call to Concat?
Calling Concat creates an iterator over both arrays. It does not create a new array so you have not used more memory for a new array. When you call ToArray you actually do create a new array and take up the memory for the new array.
So if you just need to easily iterate over both then just call Concat.
Late Answer :-).
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;
}
}
Here's my answer:
int[] z = new List<string>()
.Concat(a)
.Concat(b)
.Concat(c)
.ToArray();
This method can be used at initialization level, for example to define a static concatenation of static arrays:
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();
However, it comes with two caveats that you need to consider:
- The
Concat
method creates an iterator over both arrays: it does not create a new array, thus being efficient in terms of memory used: however, the subsequentToArray
will negate such advantage, since it will actually create a new array and take up the memory for the new array. - As @Jodrell said,
Concat
would be rather inefficient for large arrays: it should only be used for medium-sized arrays.
If aiming for performance is a must, the following method can be used instead:
/// <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;
}
Or (for one-liners fans):
int[] z = (from arrays in new[] { a, b, c } from arr in arrays select arr).ToArray();
Although the latter method is much more elegant, the former one is definitely better for performance.
For additional info, please refer to this post on my blog.
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;
}
The most efficient structure in terms of RAM (and CPU) to hold the combined array would be a special class that implements IEnumerable (or if you wish even derives from Array) and links internally to the original arrays to read the values. AFAIK Concat does just that.
In your sample code you could omit the .ToArray() though, which would make it more efficient.
Sorry to revive an old thread, but how about this:
static IEnumerable<T> Merge<T>(params T[][] arrays)
{
var merged = arrays.SelectMany(arr => arr);
foreach (var t in merged)
yield return t;
}
Then in your code:
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[]
Until you call .ToArray()
, .ToList()
, or .ToDictionary(...)
, the memory is not allocated, you are free to "build your query" and either call one of those three to execute it or simply go through them all by using foreach (var i in z){...}
clause which returns an item at a time from the yield return t;
above...
The above function can be made into an extension as follows:
static IEnumerable<T> Merge<T>(this T[] array1, T[] array2)
{
var merged = array1.Concat(array2);
foreach (var t in merged)
yield return t;
}
So in the code, you can do something like:
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[]
The rest is the same as before.
One other improvement to this would be changing T[]
into IEnumerable<T>
(so the params T[][]
would become params IEnumerable<T>[]
) to make these functions accept more than just arrays.
Hope this helps.
I've found an elegant one line solution using LINQ or Lambda expression, both work the same (LINQ is converted to Lambda when program is compiled). The solution works for any array type and for any number of arrays.
Using LINQ:
public static T[] ConcatArraysLinq<T>(params T[][] arrays)
{
return (from array in arrays
from arr in array
select arr).ToArray();
}
Using Lambda:
public static T[] ConcatArraysLambda<T>(params T[][] arrays)
{
return arrays.SelectMany(array => array.Select(arr => arr)).ToArray();
}
I've provided both for one's preference. Performance wise @Sergey Shteyn's or @deepee1's solutions are a bit faster, Lambda expression being the slowest. Time taken is dependant on type(s) of array elements, but unless there are millions of calls, there is no significant difference between the methods.
You can do it the way you have referred to, or if you want to get really manual about it, you can roll your own loop:
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];
What you need to remember is that when using LINQ you are utilizing delayed execution. The other methods described here all work perfectly, but they are executed immediately. Furthermore the Concat() function is probably optimized in ways you can't do yourself (calls to internal API's, OS calls etc.). Anyway, unless you really need to try and optimize, you're currently on your path to "the root of all evil" ;)
Try the following:
T[] r1 = new T[size1];
T[] r2 = new T[size2];
List<T> targetList = new List<T>(r1);
targetList.Concat(r2);
T[] targetArray = targetList.ToArray();
For int[] what you've done looks good to me. astander's answer would also work well for List<int>
.
For smaller arrays <10000 elements:
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) => 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;
}
}
I think the above solution is more general & lighter than the others I saw here. It is more general because it doesn't limit concatenation for only two arrays and is lighter because it doesn't use LINQ nor List.
Note the solution is concise and the added generality doesn't add significant runtime overhead.
I think a list would be apt for this purpose.
You can create a list like this.
List<int> Items = new List<int>();
Then you can pretty easily just use a for each loop to iterate over any number of arrays and add them to the list.
foreach (int i in nameOfArray)
{
Items.Add(i);
}
If you use a list it would remove the problem of an out of bounds exception. A list can be used for all the same functionality as an array. The only meaningful difference is the lack of a hard limit in the number of items.
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]);
}
var z = x.Concat(y).ToArray();
精彩评论