Indexes in C# provide direct access to an element at a specific position within an array or collection. When compared to Enumerable
methods, indexing can be more efficient for certain scenarios, such as iterating over a large collection, due to avoiding the overhead of checking the
underlying collection type before accessing it.
This applies to types that implement one of these interfaces:
We measured a significant improvement in execution time. For more details see the Benchmarks section from the More info
tab.
If the type you are using implements IList, IList<T> or IReadonlyList<T>, it implements
this[int index]. This means calls to First, Last, or ElementAt(index) can be replaced with
indexing at 0, Count-1 and index respectively.
int GetAt(List<int> data, int index)
=> data.ElementAt(index);
int GetFirst(List<int> data)
=> data.First();
int GetLast(List<int> data)
=> data.Last();
int GetAt(List<int> data, int index)
=> data[index];
int GetFirst(List<int> data)
=> data[0];
int GetLast(List<int> data)
=> data[data.Count-1];
| Method | Runtime | Mean | Standard Deviation |
|---|---|---|---|
ElementAt |
3,403.4 ns |
28.52 ns |
26.67 ns |
Index |
478.0 ns |
6.93 ns |
6.48 ns |
First |
6,160.0 ns |
57.66 ns |
53.93 ns |
First_Index |
485.7 ns |
5.81 ns |
5.15 ns |
Last |
6,034.3 ns |
20.34 ns |
16.98 ns |
Last_Index |
408.3 ns |
2.54 ns |
2.38 ns |
The results were generated by running the following snippet with BenchmarkDotNet:
private List<byte> data;
private Random random;
[Params(1_000_000)]
public int SampleSize;
[Params(1_000)]
public int LoopSize;
[GlobalSetup]
public void Setup()
{
random = new Random(42);
var bytes = new byte[SampleSize];
random.NextBytes(bytes);
data = bytes.ToList();
}
[Benchmark]
public int ElementAt()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data.ElementAt(i);
}
return result;
}
[Benchmark]
public int Index()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data[i];
}
return result;
}
[Benchmark]
public int First()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data.First();
}
return result;
}
[Benchmark]
public int First_Index()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data[0];
}
return result;
}
[Benchmark]
public int Last()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data.Last();
}
return result;
}
[Benchmark]
public int Last_Index()
{
int result = default;
for (var i = 0; i < LoopSize; i++)
{
result = data[data.Count - 1];
}
return result;
}
Hardware configuration:
BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.4412/22H2/2022Update) 11th Gen Intel Core i7-11850H 2.50GHz, 1 CPU, 16 logical and 8 physical cores .NET SDK=8.0.301 [Host] : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2 .NET 8.0 : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2