- Array
- Best Solution For Slicing Arrays
Best Solution In .NET For Slicing Arrays

Introduction
The C# array slice methods I'm going to look at are ArraySegment, Span Slice, Range Operator, and LINQ Skip Take. Each of these has pros and cons which I will get into and also test the performance of each. Slicing arrays is an important topic since a lot of data is array-based and we may not want all the data in an array but different portions of it. Some common examples of array slices could be string related since strings are an array of characters. Another place could be file-based CSV parsing and file IO operations or even parsing data from a database. We're going to start with ArraySegment for our first analysis.
Summary Table
Analysis Type | ArraySegment | Span | Range Operator | LINQ Skip Take |
---|---|---|---|---|
.NET Version Available | >= .NET 2.0 | >= .NET Core 2.1 | >= C#8.0 | >= NET 3.5 |
Lines Of Code | 1 | 1 | 1 | 1 |
Copies Array | No | Yes | No | No |
Video With Examples
ArraySegment Method For Slicing Arrays
ArraySegment has been around for a long time and has been available since .NET 2.0. It doesn't make a copy of the array but works from the original reference and must be a one-dimensional array
ArraySegment Video Example
ArraySegment Code Example
We can pass the array into the ArraySegment constructor and also give the start and length of the slice operation. This makes the slice operation fast.
string[] foodIdeasArray = new string[5] { "Spaghetti Bolognese", "Chicken Tikka Masala", "Chili Con Carne", "Beef Stroganoff", "Shepherd's Pie" };//Declare a string array
int[] intArray = new int[10] { 32, 5, 893, 898, 32, 43, 5, 59, 28, 325 };//Declare an int array
double[] doubleArray = new double[10] { 5, 13.5, 23.5, 1.5, 2.5, 494.9, 8291, 288, 2.42, 11 };//Declare a double array
float[] floatArray = new float[10] { 612f, 895f, 3f, 784f, 56f, 127f, 2f, 108f, 88f, 898f };//Declare a float array
bool[] boolArray = new bool[5] { false, false, true, true, false };//Declare a bool array
SpliceArray<string>(foodIdeasArray, 1, 3);//Splice array
SpliceArray<int>(intArray, 1, 3);//Splice array
SpliceArray<double>(doubleArray, 1, 3);//Splice array
SpliceArray<float>(floatArray, 1, 3);//Splice array
SpliceArray<bool>(boolArray, 1, 3);//Splice array
void SpliceArray<T>(T[] array, int startIndex, int endIndex)
{
Console.WriteLine($"Starting array:");
PrintArray<T>(array);
Console.WriteLine();
int count = endIndex - startIndex + 1;
ArraySegment<T> arraySegment = new ArraySegment<T>(array, startIndex, count);//ArraySegment sets the start and end places to slice the array.
//ArraySegment<T> arraySegment = new ArraySegment<T>(array);//Alternate way to slice with the same arraySegment
//arraySegment.Slice(array, startIndex, count);//Slices the arraySegment
Console.WriteLine("Sliced array:");
PrintSlicedArray<T>(arraySegment);
Console.WriteLine();
Console.WriteLine();
}
void PrintSlicedArray<T>(ArraySegment<T> arraySegment)
{
int index = arraySegment.Offset;
foreach (T item in arraySegment)
{
Console.WriteLine($"[{index++}] {item}");
}
}
void PrintArray<T>(T[] array)
{
int index = 0;
foreach (T item in array)
{
Console.WriteLine($"[{index++}] = {item}");
}
}
Code Output
Starting array:
[0] = Spaghetti Bolognese
[1] = Chicken Tikka Masala
[2] = Chili Con Carne
[3] = Beef Stroganoff
[4] = Shepherd's Pie
Sliced array:
[1] Chicken Tikka Masala
[2] Chili Con Carne
[3] Beef Stroganoff
Starting array:
[0] = 32
[1] = 5
[2] = 893
[3] = 898
[4] = 32
[5] = 43
[6] = 5
[7] = 59
[8] = 28
[9] = 325
Sliced array:
[1] 5
[2] 893
[3] 898
Starting array:
[0] = 5
[1] = 13.5
[2] = 23.5
[3] = 1.5
[4] = 2.5
[5] = 494.9
[6] = 8291
[7] = 288
[8] = 2.42
[9] = 11
Sliced array:
[1] 13.5
[2] 23.5
[3] 1.5
Starting array:
[0] = 612
[1] = 895
[2] = 3
[3] = 784
[4] = 56
[5] = 127
[6] = 2
[7] = 108
[8] = 88
[9] = 898
Sliced array:
[1] 895
[2] 3
[3] 784
Starting array:
[0] = False
[1] = False
[2] = True
[3] = True
[4] = False
Sliced array:
[1] False
[2] True
[3] True
Span Method For Slicing Arrays
Span allocates memory to the stack rather than the managed heap. This is can have an advantage in speed in some cases. It does make a copy of the data so can keep that in mind if you need to refer to the array or not. This was also release in 2018 so you'll need to check the version that you working to see if it's there.
Span Slice Method Example Code
This is similar to the ArraySegment and you can pass the parameters to the constructor or call a Slice method.
string[] foodIdeasArray = new string[5] { "Tacos", "Pad Thai", "Carbonara", "Butter Chicken", "Lasagna" };//Declare a string array
int[] intArray = new int[10] { 312, -53, 2843, 98, 80, 4303, 5909, 9, 28, 325 };//Declare an int array
double[] doubleArray = new double[10] { -50, 23.5, 033.5, -13.5, 2.5, 494.99, -82991, 289, -2.42, 11 };//Declare a double array
float[] floatArray = new float[10] { 610f, 85f, 3f, 780f, -54806f, 17f, 283f, 198f, 8884f, 8958f };//Declare a float array
bool[] boolArray = new bool[5] { true, false, false, true, true };//Declare a bool array
SpliceArray<string>(foodIdeasArray, 2, 4);//Splice array
SpliceArray<int>(intArray, 2, 4);//Splice array
SpliceArray<double>(doubleArray, 2, 4);//Splice array
SpliceArray<float>(floatArray, 2, 4);//Splice array
SpliceArray<bool>(boolArray, 2, 4);//Splice array
void SpliceArray<T>(T[] array, int startIndex, int endIndex)
{
Console.WriteLine($"Starting array:");
PrintArray<T>(array);
Console.WriteLine();
int count = endIndex - startIndex + 1;
Span<T> span = new Span<T>(array, startIndex, count);//Span sets the start and end places to slice the array.
//Span<T> span = new Span<T>(array);//Pass whole array into the span
//span.Slice(startIndex, count);//Slice sets the start and end places to slice the array.
Console.WriteLine("Sliced array:");
PrintSlicedArray<T>(span, startIndex);
Console.WriteLine();
Console.WriteLine();
}
void PrintSlicedArray<T>(Span<T> slicedArray, int startIndex)
{
int index = startIndex;
foreach (T item in slicedArray)
{
Console.WriteLine($"[{index++}] {item}");
}
}
Code Output
Starting array:
[0] = Tacos
[1] = Pad Thai
[2] = Carbonara
[3] = Butter Chicken
[4] = Lasagna
Sliced array:
[2] Carbonara
[3] Butter Chicken
[4] Lasagna
Starting array:
[0] = 312
[1] = -53
[2] = 2843
[3] = 98
[4] = 80
[5] = 4303
[6] = 5909
[7] = 9
[8] = 28
[9] = 325
Sliced array:
[2] 2843
[3] 98
[4] 80
Starting array:
[0] = -50
[1] = 23.5
[2] = 33.5
[3] = -13.5
[4] = 2.5
[5] = 494.99
[6] = -82991
[7] = 289
[8] = -2.42
[9] = 11
Sliced array:
[2] 33.5
[3] -13.5
[4] 2.5
Starting array:
[0] = 610
[1] = 85
[2] = 3
[3] = 780
[4] = -54806
[5] = 17
[6] = 283
[7] = 198
[8] = 8884
[9] = 8958
Sliced array:
[2] 3
[3] 780
[4] -54806
Starting array:
[0] = True
[1] = False
[2] = False
[3] = True
[4] = True
Sliced array:
[2] False
[3] True
[4] True
Range Operator Method For Slicing Arrays
The range operator is a completely different syntax for dealing with indexes and ranges of an array. So if you have a range of slicing requirements then the range operator should be able to accommodate you. It uses the range system which is a new syntax.
Range Operator Method Example Code
Using the range system, I provided a start and end index. I had to add 1 to the end index to get the desired number of entries consistent with the previous example.
string[] foodIdeasArray = new string[5] { "Tacos", "Pad Thai", "Carbonara", "Butter Chicken", "Lasagna" };//Declare a string array
int[] intArray = new int[10] { 312, -53, 2843, 98, 80, 4303, 5909, 9, 28, 325 };//Declare an int array
double[] doubleArray = new double[10] { -50, 23.5, 033.5, -13.5, 2.5, 494.99, -82991, 289, -2.42, 11 };//Declare a double array
float[] floatArray = new float[10] { 610f, 85f, 3f, 780f, -54806f, 17f, 283f, 198f, 8884f, 8958f };//Declare a float array
bool[] boolArray = new bool[5] { true, false, false, true, true };//Declare a bool array
SpliceArray<string>(foodIdeasArray, 2, 4);//Splice array
SpliceArray<int>(intArray, 2, 4);//Splice array
SpliceArray<double>(doubleArray, 2, 4);//Splice array
SpliceArray<float>(floatArray, 2, 4);//Splice array
SpliceArray<bool>(boolArray, 2, 4);//Splice array
void SpliceArray<T>(T[] array, int startIndex, int endIndex)
{
Console.WriteLine($"Starting array:");
PrintArray<T>(array);
Console.WriteLine();
T[] slice = array[startIndex..(endIndex + 1)];
Console.WriteLine("Sliced array:");
PrintArray<T>(slice, startIndex);
Console.WriteLine();
Console.WriteLine();
}
void PrintArray<T>(T[] array, int startIndex = 0)
{
int index = startIndex;
foreach (T item in array)
{
Console.WriteLine($"[{index++}] = {item}");
}
}
Code Output
[2] = 2843
[3] = 98
[4] = 80
[5] = 4303
[6] = 5909
[7] = 9
[8] = 28
[9] = 325
Sliced array:
[2] = 2843
[3] = 98
[4] = 80
Starting array:
[0] = -50
[1] = 23.5
[2] = 33.5
[3] = -13.5
[4] = 2.5
[5] = 494.99
[6] = -82991
[7] = 289
[8] = -2.42
[9] = 11
Sliced array:
[2] = 33.5
[3] = -13.5
[4] = 2.5
Starting array:
[0] = 610
[1] = 85
[2] = 3
[3] = 780
[4] = -54806
[5] = 17
[6] = 283
[7] = 198
[8] = 8884
[9] = 8958
Sliced array:
[2] = 3
[3] = 780
[4] = -54806
Starting array:
[0] = True
[1] = False
[2] = False
[3] = True
[4] = True
Sliced array:
[2] = False
[3] = True
[4] = True
LINQ Skip Take Method For Slicing Arrays
Skip enables it to jump to a starting point in the array while take allows for a stopping point in the array.
LINQ Skip Take Method Example Code
In this example, I want to skip to the second index and take up to the 4th index so I need to add one to the take parameter.
string[] foodIdeasArray = new string[5] { "Tacos", "Pad Thai", "Carbonara", "Butter Chicken", "Lasagna" };//Declare a string array
int[] intArray = new int[10] { 312, -53, 2843, 98, 80, 4303, 5909, 9, 28, 325 };//Declare an int array
double[] doubleArray = new double[10] { -50, 23.5, 033.5, -13.5, 2.5, 494.99, -82991, 289, -2.42, 11 };//Declare a double array
float[] floatArray = new float[10] { 610f, 85f, 3f, 780f, -54806f, 17f, 283f, 198f, 8884f, 8958f };//Declare a float array
bool[] boolArray = new bool[5] { true, false, false, true, true };//Declare a bool array
SpliceArray<string>(foodIdeasArray, 2, 4);//Splice array
SpliceArray<int>(intArray, 2, 4);//Splice array
SpliceArray<double>(doubleArray, 2, 4);//Splice array
SpliceArray<float>(floatArray, 2, 4);//Splice array
SpliceArray<bool>(boolArray, 2, 4);//Splice array
void SpliceArray<T>(T[] array, int startIndex, int endIndex)
{
Console.WriteLine($"Starting array:");
PrintArray<T>(array);
Console.WriteLine();
IEnumerable<T> slice = array.Skip(startIndex).Take(endIndex -1);//LINQ skip to the start index and take from end index
Console.WriteLine("Sliced array:");
PrintArray<T>(slice, startIndex);
Console.WriteLine();
Console.WriteLine();
}
void PrintArray<T>(IEnumerable<T> array, int startIndex = 0)
{
int index = startIndex;
foreach (T item in array)
{
Console.WriteLine($"[{index++}] = {item}");
}
}
Code Output
Starting array:
[0] = Tacos
[1] = Pad Thai
[2] = Carbonara
[3] = Butter Chicken
[4] = Lasagna
Sliced array:
[2] = Carbonara
[3] = Butter Chicken
[4] = Lasagna
Starting array:
[0] = 312
[1] = -53
[2] = 2843
[3] = 98
[4] = 80
[5] = 4303
[6] = 5909
[7] = 9
[8] = 28
[9] = 325
Sliced array:
[2] = 2843
[3] = 98
[4] = 80
Starting array:
[0] = -50
[1] = 23.5
[2] = 33.5
[3] = -13.5
[4] = 2.5
[5] = 494.99
[6] = -82991
[7] = 289
[8] = -2.42
[9] = 11
Sliced array:
[2] = 33.5
[3] = -13.5
[4] = 2.5
Starting array:
[0] = 610
[1] = 85
[2] = 3
[3] = 780
[4] = -54806
[5] = 17
[6] = 283
[7] = 198
[8] = 8884
[9] = 8958
Sliced array:
[2] = 3
[3] = 780
[4] = -54806
Starting array:
[0] = True
[1] = False
[2] = False
[3] = True
[4] = True
Sliced array:
[2] = False
[3] = True
[4] = True
Performance Test
This is a test to see the average speed over 10 tests. Each test has 100 function calls to the test method and each test method has 5 million objects. This will be the same test against all the methods to see how each compares.
ArraySegment Speed Test Code
int startIndex = 10;
int endIndex = 40;
int count = endIndex - startIndex + 1;
void TestMethod(string[] array)
{
ArraySegment<string> arraySegment = new ArraySegment<string>(array, startIndex, count);//ArraySegment sets the start and end places to slice the array.
foreach (string item in arraySegment)
{
//some logic
}
}
Span Test Code
int startIndex = 10;
int endIndex = 40;
int count = endIndex - startIndex + 1;
void TestMethod(string[] array)
{
Span<string> span = new Span<string>(array, startIndex, count);//Span sets the start and end places to slice the array.
foreach (string item in span)
{
//some logic
}
}
Range Operator Test Code
int startIndex = 10;
int endIndex = 40;
int count = endIndex - startIndex + 1;
void TestMethod(string[] array)
{
Span<string> span = new Span<string>(array, startIndex, count);//Span sets the start and end places to slice the array.
foreach (string item in span)
{
//some logic
}
}
LINQ Skip Take Operator Test Code
void TestMethod(string[] array)
{
IEnumerable<string> slice = array.Skip(startIndex).Take(endIndex -1);//LINQ skip to the start index and take from end index
foreach (string item in slice)
{
//some logic
}
}
ArraySegment Code Output
Test 1:Function Calls:100, In 0m 0s 0ms 500ns
Test 2:Function Calls:100, In 0m 0s 0ms 400ns
Test 3:Function Calls:100, In 0m 0s 0ms 200ns
Test 4:Function Calls:100, In 0m 0s 0ms 100ns
Test 5:Function Calls:100, In 0m 0s 0ms 700ns
Test 6:Function Calls:100, In 0m 0s 0ms 0ns
Test 7:Function Calls:100, In 0m 0s 0ms 200ns
Test 8:Function Calls:100, In 0m 0s 0ms 0ns
Test 9:Function Calls:100, In 0m 0s 0ms 600ns
Test 10:Function Calls:100, In 0m 0s 0ms 700ns
ArraySegment Average Speed:340ns, In 10 Tests
Span Code Output
Test 1:Function Calls:100, In 0m 0s 0ms 300ns
Test 2:Function Calls:100, In 0m 0s 0ms 200ns
Test 3:Function Calls:100, In 0m 0s 0ms 500ns
Test 4:Function Calls:100, In 0m 0s 0ms 500ns
Test 5:Function Calls:100, In 0m 0s 0ms 400ns
Test 6:Function Calls:100, In 0m 0s 0ms 600ns
Test 7:Function Calls:100, In 0m 0s 0ms 200ns
Test 8:Function Calls:100, In 0m 0s 0ms 0ns
Test 9:Function Calls:100, In 0m 0s 0ms 700ns
Test 10:Function Calls:100, In 0m 0s 0ms 300ns
Span Average Speed:370ns, In 10 Tests
Range Operator Code Output
Test 1:Function Calls:100, In 0m 0s 0ms 0ns
Test 2:Function Calls:100, In 0m 0s 0ms 700ns
Test 3:Function Calls:100, In 0m 0s 0ms 0ns
Test 4:Function Calls:100, In 0m 0s 0ms 500ns
Test 5:Function Calls:100, In 0m 0s 0ms 100ns
Test 6:Function Calls:100, In 0m 0s 0ms 900ns
Test 7:Function Calls:100, In 0m 0s 0ms 800ns
Test 8:Function Calls:100, In 0m 0s 0ms 900ns
Test 9:Function Calls:100, In 0m 0s 0ms 800ns
Test 10:Function Calls:100, In 0m 0s 0ms 800ns
Range Operator Average Speed:550ns, In 10 Tests
LINQ Skip Take Code Output
Test 1:Function Calls:100, In 0m 0s 0ms 500ns
Test 2:Function Calls:100, In 0m 0s 0ms 100ns
Test 3:Function Calls:100, In 0m 0s 0ms 800ns
Test 4:Function Calls:100, In 0m 0s 0ms 800ns
Test 5:Function Calls:100, In 0m 0s 0ms 0ns
Test 6:Function Calls:100, In 0m 0s 0ms 0ns
Test 7:Function Calls:100, In 0m 0s 0ms 700ns
Test 8:Function Calls:100, In 0m 0s 0ms 300ns
Test 9:Function Calls:100, In 0m 0s 0ms 100ns
Test 10:Function Calls:100, In 0m 0s 0ms 500ns
LINQ Skip Take Average Speed:380ns, In 10 Tests
Full ArraySegment Speed Test Code
using System.Diagnostics;
int numberOfTests = 10;//Number of tests
int numberOfFunctionCalls = 100;//Number of function calls made per test
int numberOfObjectsToCreate = 5000000;//Number test objects
int lengthOfRandomString = 50;
string testName = "ArraySegment";//Test name to print to average
int startIndex = 10;
int endIndex = 40;
int count = endIndex - startIndex + 1;
void TestMethod(string[] array)
{
ArraySegment<string> arraySegment = new ArraySegment<string>(array, startIndex, count);//ArraySegment sets the start and end places to slice the array.
foreach (string item in arraySegment)
{
//some logic
}
}
List<double> testSpeedList = new List<double>();
for (int testIndex = 0; testIndex < numberOfTests; testIndex++)
{
testSpeedList.Add(StartTest(testIndex));
}
Console.WriteLine($"{testName} Average Speed:{Math.Round(testSpeedList.Average())}ns, In {numberOfTests} Tests");
double StartTest(int testIndex)
{
Stopwatch stopwatch = new Stopwatch();
string[] testData = GetArrayData();//Get intial random generated data
for (int i = 0; i < numberOfFunctionCalls; i++)
{
stopwatch.Start();//Start the Stopwatch timer
TestMethod(testData);//
stopwatch.Stop();//Stop the Stopwatch timer
}
stopwatch.Stop();//Stop the Stopwatch timer
Console.WriteLine($"Test {testIndex + 1}:Function Calls:{numberOfFunctionCalls}, In {stopwatch.Elapsed.Minutes}m {stopwatch.Elapsed.Seconds}s {stopwatch.Elapsed.Milliseconds}ms {stopwatch.Elapsed.Nanoseconds}ns");
return stopwatch.Elapsed.Nanoseconds;
}
string[] GetArrayData()
{
string[] testData = new string[numberOfObjectsToCreate];
for (int i = 0; i < numberOfObjectsToCreate; i++)
{
string key = GenerateRandomString(lengthOfRandomString);
string value = GenerateRandomString(lengthOfRandomString);
testData[i] = value;
}
return testData;
}
string GenerateRandomString(int length)
{
Random random = new Random();
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
Conclusion
Overall Rank | Method | Speed |
---|---|---|
1 | ArraySegment | 340ns |
2 | Span | 370ns |
3 | Range Operator | 550ns |
4 | LINQ Skip Take | 380ns |
The best method for slicing an array is ArraySegment. It is a wrapper for the array and does not copy it but it is the fastest method of these and has long support through a lot of versions of .NET.
If you need something more flexible then take a look at the range operator. It has a lot of options for how to splice up the array.
LINQ Skip Take is good if you like LINQ but the syntax makes it less readable.
All of these operations are extremely fast and even under heavy testing they all did less than 1 ms which is amazing.
Know any other ways to slice an array? Let me know in the comments below.